0
点赞
收藏
分享

微信扫一扫

【Java从零到架构师第③季】【16】Spring-代理

晚安大世界 2022-01-09 阅读 24
javaspring

持续学习&持续更新中…

守破离


【Java从零到架构师第③季】【16】Spring-代理

一、生活中的一些问题

在这里插入图片描述

二、业务层的一些问题

在这里插入图片描述

在这里插入图片描述

三、静态代理

假设现在有一个用户登录的业务:

  • UserService(业务层接口):

    public interface UserService {
    
        boolean login(String username, String password);
    
    }
    
  • UserServiceImpl(核心业务的实现):

    public class UserServiceImpl implements UserService {
    
        // 核心业务
        @Override
        public boolean login(String username, String password) {
            // ...
            // dao等操作
    		// ...
            System.out.println("UserService的核心业务");
            return "lpruoyu".equals(username) && "123456".equals(password);
        }
    
    }
    
  • 控制层使用(单元测试模拟):

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="userService" class="programmer.lp.service.impl.UserServiceImpl"/>
    
    </beans>
    
        @Test
        public void userServiceTest() {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = ctx.getBean("userService", UserService.class);
            if (userService.login("lpruoyu", "lp_like_programming")) {
                System.out.println("登陆成功!");
            } else {
                System.out.println("登录失败!");
            }
        }
    

现在想要在不修改业务层和控制层代码的前提下,为登录业务增加日志记录功能,如何做?使用静态代理

  • UserServiceProxy:

    public class UserServiceProxy implements UserService {
    
        private final UserService target;
        public UserServiceProxy(UserService target) {
            this.target = target;
        }
    
        @Override
        public boolean login(String username, String password) {
            System.out.println("日志-----------------------------------start");
            boolean result = target.login(username, password);
            System.out.println("日志-----------------------------------end");
            return result;
        }
    
    }
    
  • 模拟控制层使用UserService的登录业务

    只需要修改applicationContext.xml的配置文件即可:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="userService" class="programmer.lp.service.proxy.UserServiceProxy">
            <constructor-arg>
                <bean class="programmer.lp.service.impl.UserServiceImpl"/>
            </constructor-arg>
        </bean>
    
    </beans>
    

四、代理

在这里插入图片描述

五、代理的两种实现方式

方式一:继承类

SkillService:

public class SkillService {

    public void delete(Integer id) {
        System.out.println("SkillService-delete-核心业务");
    }

}

SkillServiceProxy:

public class SkillServiceProxy extends SkillService {

    private SkillService target;

    public SkillServiceProxy(SkillService target) {
        this.target = target;
    }

    @Override
    public void delete(Integer id) {
        System.out.println("日志/事务--------------start");
        target.delete(id);
        System.out.println("日志/事务--------------end");
    }

}

使用:

    <bean id="skillService" class="programmer.lp.service.proxy.SkillServiceProxy">
        <constructor-arg>
            <bean class="programmer.lp.service.SkillService"/>
        </constructor-arg>
    </bean>
    @Test
    public void skillService() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        SkillService skillService = ctx.getBean("skillService", SkillService.class);
        skillService.delete(10);
    }

方式二:实现接口

SkillService:

public interface SkillService {

    void delete(Integer id);

}

SkillServiceImpl:

public class SkillServiceImpl implements SkillService {

    @Override
    public void delete(Integer id) {
        System.out.println("SkillService-delete-核心业务");
    }

}

SkillServiceProxy:

public class SkillServiceProxy implements SkillService {

    private SkillService target;

    public SkillServiceProxy(SkillService target) {
        this.target = target;
    }

    @Override
    public void delete(Integer id) {
        System.out.println("日志/事务--------------start");
        target.delete(id);
        System.out.println("日志/事务--------------end");
    }

}

使用:

    <bean id="skillService" class="programmer.lp.service.proxy.SkillServiceProxy">
        <constructor-arg>
            <bean class="programmer.lp.service.impl.SkillServiceImpl"/>
        </constructor-arg>
    </bean>
    @Test
    public void skillService() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        SkillService skillService = ctx.getBean("skillService", SkillService.class);
        skillService.delete(10);
    }

六、动态代理

在这里插入图片描述

注意

  • 采用接口的方式(JDK)来实现动态代理有局限性,因为并不是每个需要代理的目标对象都会实现某个或某些接口
  • 采用继承的方式(CGLib)实现动态代理则是通用的,不管目标对象是否实现了接口,都可以使用这种方式。

七、动态代理—JDK(接口)

局限性:目标对象必须实现了一个或多个接口。

在这里插入图片描述

基本使用:

        // 目标对象
        UserService target = ctx.getBean("userService", UserServiceImpl.class);
        // 代理对象
        UserService userService = (UserService) Proxy.newProxyInstance(
                this.getClass().getClassLoader(), // 类加载器,用来通过字节码创建出Class对象
                target.getClass().getInterfaces(), // 目标对象实现了的所有接口,生成的代理对象也会实现这些接口
                new InvocationHandler() { // 代理对象的附加代码
                    // 代理方法
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("proxy-------------------before");

                        // 调用目标对象的目标方法
                        Object result = method.invoke(target, args);

                        System.out.println("proxy----------------------end");
                        return result; // result:目标对象的目标方法执行后的返回值
                    }
                });
        userService.login("lpruoyu", "123456");
        userService.register("lpruoyu", "123456", "123");

LogProcessor:

// BeanPostProcessor会拦截每一个bean的生命周期
public class LogProcessor implements BeanPostProcessor {

    @Override                                    // 这个bean就是目标对象
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 为目标对象生成代理对象,并将生成的代理对象返回,将该代理对象return后
        // 控制层通过getBean方法拿到的就是目标对象的代理对象
        return Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
                new InvocationHandler() {
                    // 代理方法
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("proxy----------日志/事务...---------before");

                        // 调用目标对象的目标方法
                        Object result = method.invoke(bean, args);

                        System.out.println("proxy----------日志/事务...------------end");
                        return result;
                    }
                });
    }

}

改进一下LogProcessor:

public class LogProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return Proxy.newProxyInstance(
                getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
                new LogProcessorInvocationHandler(bean));
    }

    // 这个类就可以专心写好代理方法(附加代码)了,当然,也可以将该类单独抽取出一个.java文件
    private static class LogProcessorInvocationHandler implements InvocationHandler {
        Object target;
        LogProcessorInvocationHandler(Object target) {
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("proxy----------日志/事务...---------before");

            Object result = method.invoke(target, args);

            System.out.println("proxy----------日志/事务...------------end");
            return result;
        }
    }

}

八、动态代理—CGLib(类)

在这里插入图片描述

public class LogProcessor2 implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(getClass().getClassLoader());
        enhancer.setSuperclass(bean.getClass());
        enhancer.setCallback(new LogMethodInterceptor(bean));
        return enhancer.create();
    }

    private static class LogMethodInterceptor implements MethodInterceptor {
        Object target;
        public LogMethodInterceptor(Object target) {
            this.target = target;
        }

        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("代理-----------start-----------日志/事务...");

            Object result = method.invoke(target, args);

            System.out.println("代理-----------end-----------日志/事务...");
            return result;
        }
    }

}

注意

  • 使用BeanPostProcessor不要忘了在applicationContext.xml中注册

  • BeanPostProcessor会统一处理所有bean的生命周期,所以如果使用BeanPostProcessor来为bean设置代理对象的话,那么需要考虑清楚哪些bean需要代理,哪些bean无需代理。

  • 如何考虑?在BeanPostProcessor的postProcessAfterInitialization中判断一下即可:

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
       		// 考虑清楚哪些bean需要代理,哪些bean不需要代理
            if (!bean.getClass().getName().startsWith("programmer.lp.service.")) {
            	// 不需要代理的bean就会直接被返回
                return bean;
            }
            // 需要代理的就好执行下面的代码
            Enhancer enhancer = new Enhancer();
            enhancer.setClassLoader(getClass().getClassLoader());
            enhancer.setSuperclass(bean.getClass());
            enhancer.setCallback(new LogMethodInterceptor(bean));
            return enhancer.create();
        }
    

参考

小码哥-李明杰: Java从0到架构师③进阶互联网架构师.


本文完,感谢您的关注支持!


举报

相关推荐

0 条评论