面向切面编程

    • JFinal AOP
    • JBoot AOP
    • @Inject
    • @RPCInject
    • @Bean
    • @BeanExclude
    • @Configuration
    • @ConfigValue
    • @StaticConstruct

    JFinal AOP

    JBoot AOP

    JBoot AOP 在 JFinal AOP 的基础上,新增了我们在分布式下常用的功能,同时借鉴了 Spring AOP 的一些特征,对 JFinal AOP 做了功能的强制,但是又没有 Spring AOP 体系的复杂度。

    我们可以通过 @Inject 对任何 Bean 的属性进行注入,例如 Controller

    在以上的例子中,在默认情况下,JFinal AOP 会去实例化一个 UserService 实例,并注入到 MyController 的 userService 中,因此需要注意的是:UserService 必须是一个可以被实例化的类,不能是抽象类或者接口(Interface)。

    如果说,UserService 是一个接口,它有实现类比如 ,JFinal 提供了另一种方案,代码如下:

    1. @RequestMapping("/helloworld")
    2. public class MyController extends Controller{
    3. @Inject(UserServiceImpl.class)
    4. private UserService userService;
    5. public void index(){
    6. renderJson(userService.findAll());
    7. }
    8. }

    @Bean

    在以上的例子中,我们认为 @Inject(UserServiceImpl.class) 这可能不是最好的方案,因此,JBoot 提供了可以通过注解 @BeanUserServiceImpl.class 添加在类上,这样 Jboot 在启动的时候,会自动扫描到 UserServiceImpl.class ,并通过 JbootAopFactory 把 UserService 和 UserServiceImpl 添加上关联关系。

    代码如下:

    Controller:

    1. @RequestMapping("/helloworld")
    2. public class MyController extends Controller{
    3. @Inject
    4. private UserService userService;
    5. public void index(){
    6. renderJson(userService.findAll());
    7. }
    8. }

    UserService:

    1. public interface UserService{
    2. public List<User> findAll();
    3. }

    UserServiceImpl:

    1. @Bean
    2. public class UserServiceImpl implements UserService{
    3. public List<User> findAll(){
    4. //do sth
    5. }
    6. }

    当一个接口有多个实现类,或者当在系统中存在多个实例对象,比如有两份 Cache 对象,一份可能是 Redis Server1,一份可能是 Redis Server2,或者有两份数据源 DataSource 等,在这种情况下,我们注入的时候就需要确定注入那个实例。

    例如:

    UserService:

    1. public List<User> findAll();
    2. }

    UserServiceImpl1:

    1. @Bean(name="userServiceImpl1")
    2. public class UserServiceImpl1 implements UserService{
    3. public List<User> findAll(){
    4. //do sth
    5. }
    6. }

    UserServiceImpl2:

    1. @Bean(name="userServiceImpl2")
    2. public class UserServiceImpl2 implements UserService{
    3. public List<User> findAll(){
    4. //do sth
    5. }

    这种情况,只是针对一个接口有多个实现类的情况,那么,如果是一个接口只有一个实现类,但是有多个实例,如何进行注入呢?

    参考 @Configuration 。

    @BeanExclude

    当我们使用 @Bean 给某个类添加上注解之后,这个类会做好其实现的所有接口,但是,很多时候我们往往不需要这样做,比如:

    1. @Bean
    2. public class UserServiceImpl implements UserService,
    3. OtherInterface1,OtherInterface2...{
    4. public List<User> findAll(){
    5. //do sth
    6. }
    7. }

    在某些场景下,我们可能只希望 UserServiceImpl 和 UserService 做好映射关系,此时,@BeanExclude 就派上用场了。

    如下代码排除了 UserServiceImpl 和 OtherInterface1,OtherInterface2 的映射关系。

    在 Jboot 中的 @Configuration 和 Spring 体系的 @Configuration 功能类似。

    例如:

    1. @Configuration
    2. public class AppConfiguration {
    3. @Bean(name = "myCommentServiceFromConfiguration")
    4. public CommentService myCommentService1(){
    5. CommentService commentService = new CommentServiceImpl();
    6. return commentService;
    7. }
    8. @Bean
    9. public CommentService myCommentService2(){
    10. CommentService commentService = new CommentServiceImpl();
    11. return commentService;
    12. }
    13. }

    这样,在一个 Jboot 应用中,就会存在两份 CommentService 他们的名称分别为:myCommentServiceFromConfiguration 和 myCommentService2(当只用了注解 @Bean 但是未添加 name 参数时,name 的值为方法的名称)

    这样,我们就可以在 Controller 里,通过 @Inject 配合 @Bean(name = ... ) 进行注入,例如:

    1. @RequestMapping("/aopcache")
    2. public class AopCacheController extends JbootController {
    3. @Inject
    4. @Bean(name="myCommentService2")
    5. private CommentService commentService;
    6. @Inject
    7. @Bean(name = "myCommentServiceFromConfiguration")
    8. private CommentService myCommentService;
    9. public void index() {
    10. System.out.println("commentService:"+commentService);
    11. }
    12. }

    @ConfigValue

    在 AOP 注入中,可能很多时候我们需要注入的只是一个配置内容,而非一个对象实例,此时,我们就可以使用注解 @ConfigValue,例如:

    1. @RequestMapping("/aop")
    2. public class AopController extends JbootController {
    3. @ConfigValue("undertow.host")
    4. private String host;
    5. @ConfigValue("undertow.port")
    6. private int port;
    7. @ConfigValue(value = "undertow.xxx")
    8. private int xxx;
    9. }

    此时,配置文件 jboot.properties (包括分布式配置中心) 里配置的 undertow.host 的值自动赋值给 host 属性。其他属性同理。

    @StaticConstruct

    静态的构造方法。

    在某些类中,这个类的创建方式并不是通过 new 的方式进行创建的,或者可能构造函数是私有的,或者这个类可能是一个单例示例的类,比如:

    1. public class JbootManager {
    2. private static JbootManager me = new JbootManager();
    3. public static JbootManager me() {
    4. return me;
    5. }
    6. private JbootManager(){
    7. //do sth
    8. }
    9. }

    我们在其他类注入 JbootManager 的时候,并不希望通过 new JbootManager() 的方式进行创建注入,还是希望通过其方法 me() 进行获取。

    此时,我们就可以给 JbootManager 类添加 @StaticConstruct 注解,例如:

    但如果 JbootManager 有多个返回自己对象的静态方法,我们就可以使用 @StaticConstruct 的 value 参数来指定。

    1. @StaticConstruct("me")
    2. public class JbootManager {
    3. private static JbootManager me = new JbootManager();
    4. public static JbootManager me() {
    5. return me;
    6. }
    7. public static JbootManager create(){
    8. return new JbootManager()
    9. }
    10. private JbootManager(){
    11. //do sth
    12. }
    13. }