AOP补充案例

Spring AOP

AOP案例引入

数据库事务说明

  1. 案例分析:
    • userMapper.insert(User对象)
    • deptMapper.insert(Dept对象)
  2. 说明:由于业务需求,要求方法要么同时入库,要么同时回滚,所以必须通过事务进行控制。

Spring实现事务控制(demo)

代码结构如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OFYj6jNI-1626762937040)(SpringAOP_files/1.jpg)]

编辑UserMapper/UserMapperImpl

  1. 编辑UserMapper

     public interface UserMapper {
     	void addUser(User user);
     }
    
  2. 编辑UserMapperImpl

     @Repository
     public class UserMapperImpl implements UserMapper {
    
     	//事务的控制应该在哪一层完成? dao/mapper service
     	public void addUser(User user) {
     		System.out.println("用户入库:" + user);
     	}
    
     }
    

编辑UserService/UserServiceImpl

  1. 编辑UserService

     public interface UserService {
     	void addUser(User user);
     }
    
  2. 编辑UserServiceImpl

     @Service
     public class UserServiceImpl implements UserService {
     	@Autowired
     	private UserMapper userMapper;
    
     	//事务的控制应该放到Service层中进行控制
     	public void addUser(User user) {
     		try {
     			System.out.println("事务开始");
     			userMapper.addUser(user);
     			System.out.println("事务结束");
     		}catch (Exception e){
     			e.printStackTrace();
     			System.out.println("事务回滚");
     		}
     	}
     }
    

编辑测试类

public class TestUser {
	/**
	 * Spring中规定:
	 *  如果传入的是接口的类型,则自动查找/注入该接口的实现类
	 *  前提:该接口只有一个实现类
	 * 注入接口的原则:
	 *  if(getBean(isinterface)){
	 *      Class targetClass = interface.getImpl();
	 *      //根据类型,动态获取对象
	 *      return 对象;
	 *  }
	 */

	@Test
	public void testTx(){
		ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
		// 向上造型
		UserService userService = context.getBean(UserService.class);
//        UserService userService = (UserService) context.getBean("userServiceImpl");
		User user = new User();
		user.setId(1111);
		user.setName("SpringAOP入门案例");
		userService.addUser(user);
	}
}

代码问题分析

问题1:Service层:本应该代码业务处理即可,但是由于控制紧紧的耦合在一起

问题2:代码冗余,不便于大批量开发

解决方案:采用代理模式进行编辑

代理模式

生活中代理的案例

  • 房屋中介代理模式:

    1. 房东:自己手里有房子,需要出租换钱
    2. 中介机构:
      • 本职工作 带客户看房/出租房屋
      • 收取中介费(服务费)
    3. 租客:满足自身的需求 租房
  • 代码思维建模:

    1. 对外暴露一个公共的接口(租房子)
    2. 客户与中介机构进行沟通,中介看起来和房东功能一致(代理看起来就是展示的对象)
    3. 完成用户额外的操作(收取服务费)

组成部分

  1. 要求代理者实现与被代理者相同的接口
  2. 在代理方法中实现功能的扩展
  3. 用户调用代理对象完成功能(用户认为代理就是目标对象)

调用流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sl9X5PzQ-1626762937042)(SpringAOP_files/2.jpg)]

静态代理

通过代理模式实现事务控制

  • 角色划分:
    1. 目标对象 target UserServiceImpl类
    2. 目标方法 method addUser方法
    3. 代理:实现事务的控制
    4. 代理对象与目标对象实现相同的接口

修改目标对象名称

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hy3jf6vk-1626762937043)(SpringAOP_files/3.jpg)]

编辑代理类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Zzl6qJo-1626762937045)(SpringAOP_files/4.jpg)]

@Service("userService")
public class StaticProxy implements UserService {

	//要求引入目标的对象
	@Autowired
	private UserService target;

	//目的:对原有方法进行扩展
	public void addUser(User user) {
		try {
			System.out.println("事务开启");
			target.addUser(user);
			System.out.println("事务结束");
		}catch (Exception e){
			e.printStackTrace();
			System.out.println("事务回滚");
		}
	}
}

编辑UserServiceImpl

@Service("target")
public class UserServiceImpl implements UserService {
	@Autowired
	private UserMapper userMapper;

	//事务的控制应该放到Service层中进行控制
	public void addUser(User user) {
		userMapper.addUser(user);
	}

//    public void addUser(User user) {
//        try {
//            System.out.println("事务开始");
//            userMapper.addUser(user);
//            System.out.println("事务结束");
//        }catch (Exception e){
//            e.printStackTrace();
//            System.out.println("事务回滚");
//        }
//    }
}

编辑测试方法

@Test
public void testStaticTx(){
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = (UserService) context.getBean("userService");
    User user = new User();
    user.setId(1112);
    user.setName("测试代理机制");
    // 执行用户调用
    userService.addUser(user);
}

静态代理弊端

静态代理只针对于某个接口,不能实现所有接口的代理,实用性较差

静态代理中,所有的方法都需要手动添加事务的开始/事务提交代码,代码冗余/不够简洁

动态代理机制

动态代理分类

  1. JDK代理:
    • 要求:目标对象必须实现接口
    • 代理要求:代理对象也必须实现目标对象的接口
    • 目标对象/代理的关系:目标与代理对象是兄弟关系
  2. CGlib代理
    • 要求:不管目标对象是否有接口,都可以为其创建代理对象
    • 代理要求:要求代理对象必须继承目标对象
    • 目标对象/代理的关系:目标与代理对象是父子关系

编辑JDK动态代理

  1. 官网API:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wq5E4IV9-1626762937046)(SpringAOP_files/6.jpg)]

  2. 背会知识点:

    • 关于匿名内部类用法说明:匿名内部类引用外部参数,要求参数必须final

    • invoke():该方法标识当代理对象执行时,回调该方法

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}
      
    • 执行目标方法:

        result = method.invoke(target, args);
      
    • 目标方法执行:

        public class JDKProxyFactory {
        	// 要求用户传递目标对象
        	// 关于匿名内部类用法说明:匿名内部类引用外部参数,要求参数必须final
        	public static Object getProxy(final Object target){
        		// 调用Java API实现动态代理
        		/**
        		 * 参数分析:3个参数
        		 * 1. ClassLoader loader 类加载器(获取目标对象的Class(类型))
        		 * 2. Class<?>[] interfaces JDK代理要求必须有接口(Java中可以多实现)
        		 * 3. InvocationHandler h 对目标方法进行扩展
        		 */
        		// 获取类加载器
        		ClassLoader classLoader = target.getClass().getClassLoader();
        		// 获取接口数组
        		Class<?>[] interfaces = target.getClass().getInterfaces();
        		final Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
        			// invoke():代理对象调用方法时invoke执行,扩展方法编辑的位置
        			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        				Object result = null;
        				try {
        					// 添加事务的控制
        					System.out.println("事务开始");
        					// 执行目标方法 target代表真实的目标对象 method代表方法对象 args代表方法参数
        					// result标识目标方法执行的返回值
        					result = method.invoke(target, args);
        					System.out.println("事务提交");
        				}catch (Exception e){
        					e.printStackTrace();
        					System.out.println("事务回滚");
        				}
        				return result;
        			}
        		});
        		return  proxyInstance;
        	}
        }
      

JDK动态代理执行过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2sIVOqAv-1626762937048)(SpringAOP_files/5.jpg)]

动态代理优势

  • 将公共的部分写到动态代理中,之后其他的业务类调用即可

  • 编辑DeptService

      public interface DeptService {
      	void addDept();
      }
    
  • 编辑DeptServiceImpl

      @Service("deptService")
      public class DeptServiceImpl implements DeptService {
      	public void addDept() {
      		System.out.println("调用DeptMapper,实现入库");
      	}
      }
    
  • 编辑测试类

      public class TestDept {
      	@Test
      	public void testTx(){
      		ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
      		// 获取目标对象
      		DeptService deptService = (DeptService) context.getBean("deptService");
      		// 获取代理对象
      		DeptService proxy = (DeptService) JDKProxyFactory.getProxy(deptService);
      		// 通过代理对象 调用方法 扩展方法
      		proxy.addDept();    // 执行invoke()
      	}
      }
    

动态代理实现方案(二)

  • 业务需求:

    • 要求对Service层的方法记录其执行的时间
    • 通过执行时间长短进行针对性优化
    • 要求Service中有addUser()和delete()
    • 要求代码结构扩展性好,耦合性低
  • 编辑UserService

      public interface UserService {
      	void addUser();
      	void deleteUser();
      }
    
  • 编辑UserServiceImpl

      @Service("target")
      public class UserServiceImpl implements UserService {
      	public void addUser() {
      		System.out.println("添加用户");
      	}
    
      	public void deleteUser() {
      		System.out.println("删除用户");
      	}
      }
    
  • 编辑测试类

      public class Test {
      	@org.junit.Test
      	public void testJDKProxy(){
      		ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
      		UserService target = (UserService) context.getBean("target");
      		UserService proxy = (UserService) JDKProxyFactory.getProxy(target);
      		proxy.addUser();
      		proxy.deleteUser();
      	}
      }
    

动态代理实现方案(三)

  • 业务需求:

    • 要求进行日志控制
    • 要求方法执行时,获取方法的类名,方法民法 进行空服之台输出 以及方法执行时间
    • 要求代码结构扩展性好,耦合性低
  • 编辑DeptService

      public interface DeptService {
      	void addDept();
      	void searchDept();
      	void updateDept();
      	void deleteDept();
      }
    
  • 编辑DeptServiceImpl

      @Service("target")
      public class DeptServiceImpl implements DeptService {
      	public void addDept() {
      		System.out.println("添加部门");
      	}
    
      	public void searchDept() {
      		System.out.println("查找部门");
      	}
    
      	public void updateDept() {
      		System.out.println("修改部门");
      	}
    
      	public void deleteDept() {
      		System.out.println("删除部门");
      	}
      }
    
  • 编辑测试类

      public class Test {
      	@org.junit.Test
      	public void testProxy(){
      		ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
      		DeptService target = (DeptService) context.getBean("target");
      		DeptService proxy = (DeptService) JDKProxyFactory.getProxy(target);
      		proxy.addDept();
      		proxy.deleteDept();
      		proxy.searchDept();
      		proxy.updateDept();
      	}
      }
    

ij

public class JDKProxyFactory {
	// 要求用户传递目标对象
	// 关于匿名内部类用法说明:匿名内部类引用外部参数,要求参数必须final
	public static Object getProxy(final Object target){
		// 调用Java API实现动态代理
		/**
		 * 参数分析:3个参数
		 * 1. ClassLoader loader 类加载器(获取目标对象的Class(类型))
		 * 2. Class<?>[] interfaces JDK代理要求必须有接口(Java中可以多实现)
		 * 3. InvocationHandler h 对目标方法进行扩展
		 */
		// 获取类加载器
		ClassLoader classLoader = target.getClass().getClassLoader();
		// 获取接口数组
		Class<?>[] interfaces = target.getClass().getInterfaces();
		final Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
			// invoke():代理对象调用方法时invoke执行,扩展方法编辑的位置
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				Object result = null;
				try {
					// 添加事务的控制
					System.out.println("事务开始");
					// 执行目标方法 target代表真实的目标对象 method代表方法对象 args代表方法参数
					// result标识目标方法执行的返回值
					result = method.invoke(target, args);
					System.out.println("事务提交");
				}catch (Exception e){
					e.printStackTrace();
					System.out.println("事务回滚");
				}
				return result;
			}
		});
		return  proxyInstance;
	}
}

附AOP案例:https://blog.csdn.net/weixin_47264624/article/details/118990367

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘刘刘刘刘先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值