栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring的生命周期以及Aop简单测试

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring的生命周期以及Aop简单测试

文章目录
  • 1.基础工程创建
    • 1.创建一个maven工程
    • 2.创建一个Spring的配置类
    • 3.模拟Mapper
    • 4.业务处理
  • 2.问题及测试
    • 1.测试生命周期过程中,我们可以如何在这个过程中介入
    • 2.体验Aop编程的实现
      • 1.Spring提供了一些扩展点,如
      • 2.Jdk动态代理(此处仅以Jdk代理实现Aop为例,Cglib本质上差不多,只是他不需要实现接口,他是以生成子类对象,调用父类方法来实现的Aop)
      • 3.实现BeanPostProcessor 接口

1.基础工程创建 1.创建一个maven工程

依赖如下:

    
        
            org.springframework
            spring-context
            5.2.12.RELEASE
        
        
            org.springframework
            spring-beans
            5.2.12.RELEASE
        
        
            org.projectlombok
            lombok
            1.18.22
        
        
            org.slf4j
            slf4j-simple
            1.7.25
        
    
2.创建一个Spring的配置类
@ComponentScan("cn.redocloud.service")
public class AppConfig {
}

这里需要注意一点,需要放进Spring容器;里或者使用了Spring注解的类,如果不再同一包下时,如果新增一个类,不再原有包下时,需要添加扫描路径,如我最终的扫包为如下

@ComponentScan({"cn.redocloud.service","cn.redocloud.mapper","cn.redocloud.config"})
public class AppConfig {
}

其中,"cn.redocloud.service"为我存放业务类的包(主要做包扫描,生成对象后放入Sping容器)

3.模拟Mapper

接口声明

public interface UserMapper {
    
    List getUser();
}

模拟实现

@Repository
public class UserMapperImpl implements UserMapper {
    @Override
    public List getUser() {
        List users = new ArrayList<>();
        User user = User.builder().username("1800000000").password("123456").build();
        users.add(user);
        return users;
    }
}
4.业务处理

业务接口

public interface UserService {
    
    String login(String username,String password);
}

实现类

@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Override
    public String login(String username, String password) {
        log.info("用户{},执行登录",username);
        return username;
    }
}
2.问题及测试

前置,测试类如下

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = applicationContext.getBean(UserService.class);
        userService.login("1800000000","123456");
    }
}
1.测试生命周期过程中,我们可以如何在这个过程中介入

修改UserServiceImpl为如下:

@Service
@Slf4j
public class UserServiceImpl implements UserService, InitializingBean, BeanNameAware , ApplicationContextAware , BeanFactoryAware {


    @Autowired
    UserMapper userMapper;

    private Map userCache = new ConcurrentHashMap<>(10);

    public UserServiceImpl(){
        log.info("这是UserServiceImpl构造方法,userMapper为{}",userMapper);
    }
    @PostConstruct
    public void postConstruct(){
        log.info("这是PostConstruct方法,userMapper为{}",userMapper);
    }

    @Override
    public String login(String username, String password) {
        log.info("用户{},执行登录",username);
        User user = userCache.get(username);
        log.info("该用户完整信息为{}",user);
        return username;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        List users = userMapper.getUser();
        if(!CollectionUtils.isEmpty(users)){
            users.forEach(user -> userCache.put(user.getUsername(),user));
        }
        log.info("执行初始化方法afterPropertiesSet,userMapper为{}",userMapper);
    }

    @Override
    public void setBeanName(String s) {
        log.info("beanNameAware名:{},userMapper为{}",s,userMapper);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("applicationContextAware,userMapper为{}",userMapper);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("beanFactoryAware,userMapper为{}",userMapper);
    }
}

如上,分別在构造方法、 @PostConstruct注解方法、InitializingBean接口的afterPropertiesSet以及三种Aware接口的子接口BeanNameAware的setBeanName、ApplicationContextAware 接口的setApplicationContext方法、BeanFactoryAware的setBeanFactory接口中打印一些话
测试结果如下:

[main] INFO cn.redocloud.service.impl.UserServiceImpl - 这是UserServiceImpl构造方法,userMapper为null
[main] INFO cn.redocloud.service.impl.UserServiceImpl - beanNameAware名:userServiceImpl,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@646007f4
[main] INFO cn.redocloud.service.impl.UserServiceImpl - beanFactoryAware,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@646007f4
[main] INFO cn.redocloud.service.impl.UserServiceImpl - applicationContextAware,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@646007f4
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 这是PostConstruct方法,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@646007f4
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 执行初始化方法afterPropertiesSet,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@646007f4
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 用户1800000000,执行登录
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 该用户完整信息为User(username=1800000000, password=123456)

说明这些情况下的执行顺序为:构造方法=>BeanNameAware=>BeanFactoryAware=>ApplicationContextAware=>@PostConstruct=>InitializingBean

2.体验Aop编程的实现 1.Spring提供了一些扩展点,如

BeanPostProcessor 接口

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

如上,上面只是单纯得返回的是传进来的bean,而在这个时候,我们其实可以生成一个代理对象返回,也就实现了我们的Aop

2.Jdk动态代理(此处仅以Jdk代理实现Aop为例,Cglib本质上差不多,只是他不需要实现接口,他是以生成子类对象,调用父类方法来实现的Aop)

代理时,我们的被代理类(UserServiceImpl)中加入一个注解,如下

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Inherited
public @interface Required {
}

原UserServiceImpl修改为(只是修改了打印的提示语,外加在login方法上加上了我们新增的Required注解)

@Service
@Slf4j
public class UserServiceImpl implements UserService, InitializingBean, BeanNameAware , ApplicationContextAware , BeanFactoryAware {


    @Autowired
    UserMapper userMapper;

    private Map userCache = new ConcurrentHashMap<>(10);

    public UserServiceImpl(){
        log.info("这是UserServiceImpl构造方法,userMapper为{}",userMapper);
    }
    @PostConstruct
    public void postConstruct(){
        log.info("这是PostConstruct方法,userMapper为{}",userMapper);
    }

    @Override
    @Required
    public String login(String username, String password) {
        log.info("用户{},执行登录",username);
        User user = userCache.get(username);
        log.info("该用户完整信息为{}",user);
        return username;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        List users = userMapper.getUser();
        if(!CollectionUtils.isEmpty(users)){
            users.forEach(user -> userCache.put(user.getUsername(),user));
        }
        log.info("执行InitializingBean接口的初始化方法afterPropertiesSet,userMapper为{}",userMapper);
    }

    @Override
    public void setBeanName(String s) {
        log.info("执行beanNameAware的setBeanName方法,Bean的名称为:{},userMapper为{}",s,userMapper);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("执行applicationContextAware接口的setApplicationContext方法,userMapper为{}",userMapper);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("执行beanFactoryAware的setBeanFactory方法,userMapper为{}",userMapper);
    }
}

工具类,从被代理对象中获取到注解信息

public class DynamicProxyAnnotationUtil {

	public static boolean findAnnotationFromProxy(Class cusAnnotation, Object target, Method method) throws NoSuchMethodException {
		Annotation[] annotations = target.getClass().getMethod(method.getName(), method.getParameterTypes()).getAnnotations();
		boolean hasAnnotation = Arrays.stream(annotations).filter(annotation -> {
			return annotation.annotationType().isAssignableFrom(cusAnnotation);
		}).count() > 0;
		return hasAnnotation;
	}
}

代理对象生成类

@Slf4j
public class AopProxy implements InvocationHandler {
    private Object target;

    public AopProxy(Object target){
        this.target = target;
    }


    public Object createProxy(Object target) {
        this.target = target;
        return createProxy();
    }

    public Object createProxy() {
        if(target == null){
            throw new RuntimeException("target object can not be null");
        }
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }


    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        boolean required = DynamicProxyAnnotationUtil.findAnnotationFromProxy(Required.class,target,method);
        if(required){
        //这里只做模拟,Spring中加入了AspectJ的概念和注解,你看,我们这里可以拿到对象,也可以拿到方法,所以字段、方法上有什么我们都能获取到,要做点什么都很好办吧,这不就是Spring里面的@Before方法吗
            log.info("UserService调用前做的增强···");
        }
        //这个地方是不是可能会抛出异常,这不就是Spring中的@AfterThrowing吗
        Object result = method.invoke(target, args);
        if(required){
        //你看,这不就是我们处理完以后的方法吗@After
            log.info("UserService调用后做的增强···");
        }
        return result;
    }

    public Object getTarget() {
        return target;
    }
}
3.实现BeanPostProcessor 接口

我这里做了一个判断,就是属于UserService的实现类时,才需要做一些打印或者处理(主要目的是查看Bean生成的完整顺序)

@Slf4j
@Component
public class AppBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof UserService){
            log.info("AppBeanPostProcessor=>postProcessBeforeInitialization:beanName {}",beanName);
        }
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 为当前 bean 对象注册监控代理对象,负责增强 bean 对象方法的能力
        Class beanClass = bean.getClass();
        if (bean instanceof UserService) {
            log.info("AppBeanPostProcessor=>postProcessAfterInitialization:beanName {},类名为 {}",beanName,beanClass.getName());
            //JDK生成代理类
            Object proxy = new AopProxy(bean).createProxy();
            log.info("代理类{}",proxy.getClass().getName());
            return proxy;
        }
        return bean;
    }
}

此时的运行结果如下:

[main] INFO cn.redocloud.service.impl.UserServiceImpl - 这是UserServiceImpl构造方法,userMapper为null
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 执行beanNameAware的setBeanName方法,Bean的名称为:userServiceImpl,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@df27fae
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 执行beanFactoryAware的setBeanFactory方法,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@df27fae
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 执行applicationContextAware接口的setApplicationContext方法,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@df27fae
[main] INFO cn.redocloud.config.AppBeanPostProcessor - AppBeanPostProcessor=>postProcessBeforeInitialization:beanName userServiceImpl
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 这是PostConstruct方法,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@df27fae
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 执行InitializingBean接口的初始化方法afterPropertiesSet,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@df27fae
[main] INFO cn.redocloud.config.AppBeanPostProcessor - AppBeanPostProcessor=>postProcessAfterInitialization:beanName userServiceImpl,类名为 cn.redocloud.service.impl.UserServiceImpl
[main] INFO cn.redocloud.config.AppBeanPostProcessor - 代理类com.sun.proxy.$Proxy13
[main] INFO cn.redocloud.config.AopProxy - UserService调用前做的增强···
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 用户1800000000,执行登录
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 该用户完整信息为User(username=1800000000, password=123456)
[main] INFO cn.redocloud.config.AopProxy - UserService调用后做的增强···

可以看到Spring提供的BeanPostProcessor扩展点对生命周期的影响节点为Aware接口后,@PostConstruct前;以及InitializingBean提供的afterPropertiesSet接口后
最后生成的代理类:

通过上面的运行结果,也就可以看出在AopProxy中加入的两个打印也生效了

由于UserServiceImpl方法中只有一个login方法,且加上了@Required注解,现在新增一个接口,测试下看是否会影响结果:
UserServiceImpl类新增方法

    @Override
    public String test() {
        log.info("test");
        return "test";
    }

此方法上没有添加我们自定义的注解@Required
修改测试类为如下

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = applicationContext.getBean(UserService.class);
//        userService.login("1800000000","123456");
        userService.test();
    }
}

执行结果

[main] INFO cn.redocloud.service.impl.UserServiceImpl - 这是UserServiceImpl构造方法,userMapper为null
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 执行beanNameAware的setBeanName方法,Bean的名称为:userServiceImpl,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@df27fae
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 执行beanFactoryAware的setBeanFactory方法,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@df27fae
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 执行applicationContextAware接口的setApplicationContext方法,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@df27fae
[main] INFO cn.redocloud.config.AppBeanPostProcessor - AppBeanPostProcessor=>postProcessBeforeInitialization:beanName userServiceImpl
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 这是PostConstruct方法,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@df27fae
[main] INFO cn.redocloud.service.impl.UserServiceImpl - 执行InitializingBean接口的初始化方法afterPropertiesSet,userMapper为cn.redocloud.mapper.impl.UserMapperImpl@df27fae
[main] INFO cn.redocloud.config.AppBeanPostProcessor - AppBeanPostProcessor=>postProcessAfterInitialization:beanName userServiceImpl,类名为 cn.redocloud.service.impl.UserServiceImpl
[main] INFO cn.redocloud.config.AppBeanPostProcessor - 代理类com.sun.proxy.$Proxy13
[main] INFO cn.redocloud.service.impl.UserServiceImpl - test

以上则说明没有@Required注解时,是不会通过我们编写的额外逻辑(Aop)的,Spring声明式事务的实现也许就是如此

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/847301.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号