依赖注入/切面编程
#
控制反转(IoC)/ 依赖注入(DI)控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
#
注册bean到IoC容器#
Component/Service注解本框架保持了和Spring常用方式的兼容,但只支持@Component和@Service两个注解,暂未支持@Repository,所以语意上来说,本框架中,注册的bean要么是组件(Component)要么是服务(Service)。
- 任何一个可以实例化的类加上@Component或@Service注解,即可自动注册到IOC容器中,如果该类拥有父类或接口类,还会自动进行与父类、接口类的映射。
- 如果接口或父类有多个子实现类,且需要用该父类或接口类型进行依赖注入,那么子实现类注册的时候需要加上名子区别彼此,否则依赖注入时无法确定注入哪一个实例。
- 任何一个可以实例化的类如果确定为独立使用类,无需和任何父类或接口类映射,也可以不注册,依赖注入时会自动注册该类。
- 所有注册的类默认会被实例化为单例,如果该类需要每次注入时创建一个新实例,那么需要在该类上加上@Scope(ScopeType.prototype)注解明确指定。
#
Configuration/Bean注解和Spring类似,支持通过@Configuration和@Bean注解进行应用配置,这里配置的bean有最高优先级,可以替换已经通过其他方式注册的类。通过这种方式我们可以在不改动原有设计或模块的情况下,从外部替换注入的组件。 通过这种方式配置bean的时候可以指定bean的initMethod和destroyMethod,分别对应于bean创建后和销毁前。由于尚未实现PostConstruct和PreDestroy注解,目前可以用这种方式替代。
#
通过程序手动注册如果开发过程中需要主动指定映射关系和注册实例,可以通过以下方式进行:
- 注册映射关系可以通过Ready.beanManager().addMapping系列方法进行操作。
注册映射关系并指定实例,通过以下方法进行操作:
- Ready.beanManager().addSingletonObject(targetObject); // 直接注册该targetObject的类并映射到targetObject实例
- Ready.beanManager().addSingletonObject(target.class, targetObject); // 直接注册target类并映射到targetObject实例
如果注册类已经注册过了,则会失败并抛出异常。
#
Autowired/Inject注解@Autowired和@Inject注解都可以实现依赖注入,区别是@Autowired需要搭配@Qualifier注解来选择注入对象,而@Inject可以直接通过名字或类来选择注入对象。
#
综合实例- 定义一个接口类
- 实现一个Cat类并通过@Component("cat")注解注册,因为我们还会创建多个子类,所以这里注册时加上名称。
- 实现一个dog类并通过@Service("dog")注解注册,因为Animal有多个子类,所以这里注册时加上名称。
- 实现一个pig类并通过@Component注解注册,但这里注册时刻意没有给pig类添加注册名。
- 实现一个Sheep类,但并未进行注解注册。
- 创建一个Configuration配置类来注册上面还没有注册的sheep类,并对已经注册的pig类进行替换。
- 创建一个Controller来综合应用以上创建的接口和实现类。
#
面向切面编程(AOP)AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 拦截器是AOP的最典型的应用的方式之一,它可以在不修改原有模块代码的情况下,动态切入模块对目标类或方法进行干预。
#
简单拦截器简单拦截器继承自Jfinal,需要实现一个拦截器并在目标类或方法上添加@Before指向拦截器。从而实现对目标方法执行前和执行后进行干预。
- 实现拦截器
创建一个类实现Interceptor接口的intercept方法:
- 应用拦截器
本来该Action输出的是"123456",应用@Before(FirstInterceptor.class)拦截器后,输出变为"654321"。如果需要,也可以通过程序为特定路由添加拦截器。
- 全局拦截器(不推荐)
如果需要批量对某些类进行操作,可以不用每个类去添加@Before注解,只需要设计一个拦截器,然后给拦截器加上@GlobalInterceptor全局拦截注解即可。
我们来看一下拦截效果
访问globalInterceptor,可以发现 Cat 和 Dog 的 walk 和 eat 方法被修改了,输出内容多了 "happily" 修饰。
但并不推荐使用全局拦截器,除了性能方面的考虑,下面还有更高级的拦截器可供选择。
#
自定义注解拦截组件除了应用框架集成的拦截器组件,也可以轻松开发自己的拦截器组件。
- 定一个注解
- 实现一个简单拦截器
- 注册你的组件
- 应用你的注解拦截组件
随便找一个组件或控制器,加上你的注解。这里以Cat类的walk方法测试。
运行并执行Cat的walk方法,你会看到walk方法被你的组件干预了。
#
高级拦截器上面的简单拦截器拦截的目标类必须是通过框架的依赖注入才能有效,比较受限制。而高级拦截器则可以拦截几乎所有需要拦截的类,没有依赖注入限制。 下面两种方式皆可实现高级拦截器,推荐用第一种方式,还支持通过配置文件进行动态配置。
- 实现EasyInterceptor接口类
然后在配置文件中添加拦截器配置,设定拦截器以及目标类(Pig)和方法(sleep),如下:
- 继承AbstractMethodInterceptor类,实现以下方法:
由于这种方式不能进行配置设定,所以需要手工加载,这里选择在配置设定的时候一起加载。
#
动态修改JAVA代码本框架提供一个非常便捷的机制对已有组件进行Java代码级别的动态修改。注意不是真的修改原class的Java代码或字节码,是通过AOP手段实现,目标对象必须通过依赖注入才有效。
- 实现一个代码注入注解
- 实现一个代码生成器
生成需要在目标方法前插入的Java代码、方法后追加的Java代码,或者替换整个方法的Java代码。
- 注册代码生成组件
上面注册了代码生成组件,指定范围是Animal的子类,下面随便找一个Animal的子类的方法,加上CodeInjector注解。
- 应用代码生成组件
我们在Cat类的eat方法上加上注解,可以切换replace = true和false来观察对该方法的修改结果。
#
更多文档以上为本框架提供的常用IoC/AOP应用介绍,还有更多IoC/AOP方面的文档将在后续不断完善,重点内容将以专题形式提供。