Spring AOP 学习(动态代理、JdbcTemplate、Junit)

news/2024/11/25 17:29:34/

动态代理

Proxy  jdk动态代理,面向接口

cglib   第三方动态代理,面向父类

jdk动态代理

public class Test1 {public static void main(String[] args) {Dinner dinner=new Person("张三");// 通过Porxy动态代理获得一个代理对象,在代理对象中,对某个方法进行增强
//        ClassLoader loader,被代理的对象的类加载器ClassLoader classLoader = dinner.getClass().getClassLoader();
//        Class<?>[] interfaces,被代理对象所实现的所有接口Class[] interaces= dinner.getClass().getInterfaces();
//        InvocationHandler h,执行处理器对象,专门用于定义增强的规则InvocationHandler handler = new InvocationHandler(){// invoke 当我们让代理对象调用任何方法时,都会触发invoke方法的执行public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                Object proxy, 代理对象
//                Method method,被代理的方法
//                Object[] args,被代理方法运行时的实参Object res=null;if(method.getName().equals("eat")){System.out.println("饭前洗手");// 让原有的eat的方法去运行res =method.invoke(dinner, args);System.out.println("饭后刷碗");}else{// 如果是其他方法,那么正常执行就可以了res =method.invoke(dinner, args);}return res;}};Dinner dinnerProxy =(Dinner) Proxy.newProxyInstance(classLoader,interaces,handler);//dinnerProxy.eat("包子");dinnerProxy.drink();}
}
interface Dinner{void eat(String foodName);void drink();
}
class Person implements Dinner{private String name;public Person(String name) {this.name = name;}@Overridepublic void eat(String foodName) {System.out.println(name+"正在吃"+foodName);}@Overridepublic void drink( ) {System.out.println(name+"正在喝茶");}
}
class Student implements Dinner{private String name;public Student(String name) {this.name = name;}@Overridepublic void eat(String foodName) {System.out.println(name+"正在食堂吃"+foodName);}@Overridepublic void drink( ) {System.out.println(name+"正在喝可乐");}
}

不修改原有代码,或者没有办法修改原有代码的情况下,增强对象功能,使用代理对象代替原来的对象去完成功能,进而达到拓展功能的目的

JDK Proxy 动态代理是面向接口的动态代理,一定要有接口和实现类的存在,代理对象增强的是实现类实现接口时重写的方法 

生成的代理对象只能转换成接口,不能转换成被代理类(上面只能是Dinner,不能是Person或Student)

代理对象只能增强接口中定义的方法, 实现类中其他和接口无关的方法是无法增强的

代理对象只能读取到接口中方法上的注解,不能读取到实现类方法上的注解

cglib动态代理

面向父类的,和接口没有直接关系

不仅仅可以增强接口中定义的方法,还可以增强一个类的其他方法

可以读取父类中方法上的所有注解

public class Test1 {@Testpublic void testCglib(){Person person =new Person();// 1 获得一个Enhancer对象Enhancer enhancer=new Enhancer();// 2 设置父类字节码enhancer.setSuperclass(person.getClass());// 3 获取MethodIntercepter对象 用于定义增强规则MethodInterceptor methodInterceptor=new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {/*Object o,  生成之后的代理对象 personProxyMethod method,  父类中原本要执行的方法  Person>>> eat()Object[] objects, 方法在调用时传入的实参数组MethodProxy methodProxy  子类中重写父类的方法 personProxy >>> eat()*/Object res =null;if(method.getName().equals("eat")){// 如果是eat方法 则增强并运行System.out.println("饭前洗手");res=methodProxy.invokeSuper(o,objects);System.out.println("饭后刷碗");}else{// 如果是其他方法 不增强运行res=methodProxy.invokeSuper(o,objects); // 子类对象方法在执行,默认会调用父类对应被重写的方法}return res;}};// 4 设置methodInterceptorenhancer.setCallback(methodInterceptor);// 5 获得代理对象Person personProxy = (Person)enhancer.create();// 6 使用代理对象完成功能personProxy.eat("包子");}
}
class Person  {public Person( ) {}public void eat(String foodName) {System.out.println("张三正在吃"+foodName);}
}

AOP概念

AOP切面编程一般可以帮助我们在不修改现有代码的情况下,对程序的功能进行拓展,往往用于实现日志处理,权限控制,性能检测,事务控制


AOP实现的原理就是动态代理

在有接口的情况下,使用JDK动态代理,在没有接口的情况下使用cglib动态代理

开闭原则:对于扩展是开放的,但是对于修改是封闭的

连接点 Joint point

类里面那些可以被增强的方法,这些方法称之为连接点(可以被增强,不一定真的被增强了)

 切入点 Pointcut

实际被增强的方法,称之为切入点

通知 Advice

实际增强的逻辑部分称为通知 (增加的功能)

通知类型: 1 前置通知 2 后置通知 3 环绕通知 4 异常通知 5 最终通知

目标对象 Target

被增强功能的对象(被代理的对象)

织入 Advice 的目标对象

切面Aspect

表现为功能相关的一些advice方法放在一起声明成的一个Java类

织入 Weaving

创建代理对象并实现功能增强的声明并运行过程

切入点表达式

execution([权限修饰符][返回值类型][类的全路径名][方法名](参数 列表) )

execution(* com.msb.dao.UserDaoImpl.add(..))  //指定切点为UserDaoImpl.add方法 

execution(* com.msb.dao.UserDaoImpl.*(..))      //指定切点为UserDaoImpl.所有的方法 

execution(* com.msb.dao.*.*(..))                         //指定切点为dao包下所有的类中的所有的方法 

execution(* com.msb.dao.*.add(..))                     // 指定切点为dao包下所有的类中的add的方法 

execution(* com.msb.dao.*.add*(..))                   // 指定切点为dao包下所有的类中的add开头的方法 

通知类型

前置通知@Before

切点方法执行之前先执行的功能,参数列表可以用JoinPoint接收切点对象

后置通知@After

方法执行之后要增强的功能,无论切点方法是否出现异常都会执行的方法(最终通知)

返回通知@AfterReturning

切点方法正常运行结束后增强的功能,

    @AfterReturning( value = "execution(* com.msb.dao.UserDaoImpl.add(..))",returning = "res")public void methodAfterReturning(JoinPoint joinPoint,Object res){System.out.println("AfterReturning invoked");}

如果方法运行过程中出现异常,则该功能不运行

参数列表可以用 JoinPoint joinPoint接收切点对象
可以用Object res接收方法返回值,需要用returning指定返回值名称

异常通知 @AfterThrowing

切点方法出现异常时运行的增强功能,如果方法运行没有出现异常,则该功能不运行

参数列表可以用Exception ex接收异常对象 需要通过throwing指定异常名称

    @AfterThrowing( value = "execution(* com.msb.dao.UserDaoImpl.add(..))",throwing = "ex")public void methodAfterThrowing(Exception ex){System.out.println("AfterThrowing invoked");}

环绕通知@Around

在切点方法之前和之后都进行功能的增强

需要在通知中定义方法执行的位置,并在执行位置之前和之后自定义增强的功能

方法列表可以通过ProceedingJoinPoint获取执行的切点

通过proceedingJoinPoint.proceed()方法控制切点方法的执行位置

proceedingJoinPoint.proceed()方法会将切点方法的返回值获取到,可以用来做后续处理

我们在环绕通知的最后需要将切点方法的返回值继续向上返回,否则切点方法在执行时接收不到返回值

@Around("execution(* com.msb.dao.UserDaoImpl.add(..))")public Object methodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("aroundA invoked");Object proceed = proceedingJoinPoint.proceed();System.out.println("aroundB invoked");return proceed;}

公共切点

定义

    @Pointcut("execution(* com.msb.dao.*.add*(..))")public void addPointCut(){}

使用

@Before("addPointCut()")

通知顺序:@Around before =》@Before =》方法调用=》@AfterReturning/@AfterThrowing=》@After=》@Around after

 @Order

可以指定代理顺序,数字越小,越靠后被代理,也就是@Around before越先执行

@EnableAspectJAutoProxy(proxyTargetClass=true)

开启自动代理

使用aop需要开启包扫描和开启自动代理

JdbcTemplate

可以使用JdbcTemplate实现查询

// 查询个数
Integer empCount = jdbcTemplate.queryForObject("select count(1) from emp", Integer.class);
// 查询单个对象
BeanPropertyRowMapper<Emp> rowMapper =new BeanPropertyRowMapper<>(Emp.class);
Emp emp = jdbcTemplate.queryForObject("select * from emp where empno =?", rowMapper, empno);

插入 

        // 批量新增操作String sql ="insert into dept values(DEFAULT,?,?)";List<Object[]> args =new LinkedList<>();for (Dept dept : depts) {Object[] arg ={dept.getDname(),dept.getLoc()};args.add(arg);}return jdbcTemplate.batchUpdate(sql, args);

查看接口实现类

ctrl + h

事务

@Transactional注解放在类上,表示该类中所有方法都加事务

加了@Transactional注解后,该方法中两个dao操作会保持原子性

@Transactional的参数

public @interface Transactional {/*** Alias for {@link #transactionManager}.* @see #transactionManager*/@AliasFor("transactionManager")String value() default "";/*** A <em>qualifier</em> value for the specified transaction.* <p>May be used to determine the target transaction manager, matching the* qualifier value (or the bean name) of a specific* {@link org.springframework.transaction.TransactionManager TransactionManager}* bean definition.* @since 4.2* @see #value* @see org.springframework.transaction.PlatformTransactionManager* @see org.springframework.transaction.ReactiveTransactionManager*/@AliasFor("value")String transactionManager() default "";/*** The transaction propagation type.* <p>Defaults to {@link Propagation#REQUIRED}.* @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()*/Propagation propagation() default Propagation.REQUIRED;/*** The transaction isolation level.* <p>Defaults to {@link Isolation#DEFAULT}.* <p>Exclusively designed for use with {@link Propagation#REQUIRED} or* {@link Propagation#REQUIRES_NEW} since it only applies to newly started* transactions. Consider switching the "validateExistingTransactions" flag to* "true" on your transaction manager if you'd like isolation level declarations* to get rejected when participating in an existing transaction with a different* isolation level.* @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel()* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction*/Isolation isolation() default Isolation.DEFAULT;/*** The timeout for this transaction (in seconds).* <p>Defaults to the default timeout of the underlying transaction system.* <p>Exclusively designed for use with {@link Propagation#REQUIRED} or* {@link Propagation#REQUIRES_NEW} since it only applies to newly started* transactions.* @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()*/int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;/*** A boolean flag that can be set to {@code true} if the transaction is* effectively read-only, allowing for corresponding optimizations at runtime.* <p>Defaults to {@code false}.* <p>This just serves as a hint for the actual transaction subsystem;* it will <i>not necessarily</i> cause failure of write access attempts.* A transaction manager which cannot interpret the read-only hint will* <i>not</i> throw an exception when asked for a read-only transaction* but rather silently ignore the hint.* @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()* @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly()*/boolean readOnly() default false;/*** Defines zero (0) or more exception {@link Class classes}, which must be* subclasses of {@link Throwable}, indicating which exception types must cause* a transaction rollback.* <p>By default, a transaction will be rolling back on {@link RuntimeException}* and {@link Error} but not on checked exceptions (business exceptions). See* {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)}* for a detailed explanation.* <p>This is the preferred way to construct a rollback rule (in contrast to* {@link #rollbackForClassName}), matching the exception class and its subclasses.* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}.* @see #rollbackForClassName* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)*/Class<? extends Throwable>[] rollbackFor() default {};/*** Defines zero (0) or more exception names (for exceptions which must be a* subclass of {@link Throwable}), indicating which exception types must cause* a transaction rollback.* <p>This can be a substring of a fully qualified class name, with no wildcard* support at present. For example, a value of {@code "ServletException"} would* match {@code javax.servlet.ServletException} and its subclasses.* <p><b>NB:</b> Consider carefully how specific the pattern is and whether* to include package information (which isn't mandatory). For example,* {@code "Exception"} will match nearly anything and will probably hide other* rules. {@code "java.lang.Exception"} would be correct if {@code "Exception"}* were meant to define a rule for all checked exceptions. With more unusual* {@link Exception} names such as {@code "BaseBusinessException"} there is no* need to use a FQN.* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}.* @see #rollbackFor* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)*/String[] rollbackForClassName() default {};/*** Defines zero (0) or more exception {@link Class Classes}, which must be* subclasses of {@link Throwable}, indicating which exception types must* <b>not</b> cause a transaction rollback.* <p>This is the preferred way to construct a rollback rule (in contrast* to {@link #noRollbackForClassName}), matching the exception class and* its subclasses.* <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}.* @see #noRollbackForClassName* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)*/Class<? extends Throwable>[] noRollbackFor() default {};/*** Defines zero (0) or more exception names (for exceptions which must be a* subclass of {@link Throwable}) indicating which exception types must <b>not</b>* cause a transaction rollback.* <p>See the description of {@link #rollbackForClassName} for further* information on how the specified names are treated.* <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}.* @see #noRollbackFor* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)*/String[] noRollbackForClassName() default {};}

事务的传播行为:多事务方法之间调用,事务是如何管理的

Junit用法

spring中junit4

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:xxx.xml")
public class Test05 {
}

spring中junit5

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:xxx.xml")
public class Test06 {
}

相当于

@SpringJUnitConfig(locations = "xxx.xml")
public class Test06 {
}


http://www.ppmy.cn/news/276691.html

相关文章

一、kafka入门

Kafka入门 为什么要用消息中间件&#xff1f; 异步处理 场景说明&#xff1a;用户注册后&#xff0c;需要发注册邮件和注册短信。传统的做法有两种1.串行的方式&#xff1b;2.并行方式。 串行方式&#xff1a;将注册信息写入数据库成功后&#xff0c;发送注册邮件&#xff…

电子小字典

电子小字典 // author: Folivora Li // copyright: Folivora Li/*24、【3】电子小字典&#xff08;选做&#xff09;&#xff08;查找&#xff09; [问题描述] 利用键树结构&#xff0c;建立一个微型电子字典。 [基本要求] 实现生词的加入&#xff0c;单词的查找、删除&#x…

C++实现电子词典(简单小程序)

电子词典&#xff1a;输入单词&#xff0c;显示单词的汉语翻译 思路分析&#xff1a;1.定义一个map<word,中文翻译> 对象 的map对象 2.下载英汉翻译文档&#xff0c;一行一行读取文档内信息&#xff0c;单词和中文翻译 3.读文件&#xff1a;获取单词&#xff0c;中文…

电子小字典(南航2022数据结构课程设计第十八题)

[问题描述] 利用键树结构&#xff0c;建立一个微型电子字典。 [基本要求] 实现生词的加入&#xff0c;单词的查找、删除&#xff0c;修改等操作。 [基本思路] 这道题的关键就是键树的操作&#xff0c;键树大概就是下图的样子&#xff0c;每个节点存一个字母&#xff0c;多…

字库在嵌入式系统使用

随着现代电子与信息技术的不断发展&#xff0c;信息处理产品的种类越来越多。常 见的涉及信息处理的软件产品和嵌入式系统包括&#xff1a;桌面操作系统&#xff08;Windows、Mac、Linux、Unix等&#xff09;、具有文字处理功能的软件&#xff08;Office、CAD、OA等&#xff09…

屏幕取词的原理

“鼠标屏幕取词”技术是在电子字典中得到广泛地应用的&#xff0c;如四通利方和金山词霸等软件&#xff0c;这个技术看似简单&#xff0c;其实在windows系统中实现却是非常复杂的&#xff0c;总的来说有两种实现方式&#xff1a; 第一种&#xff1a;采用截获对部分gdi的api调用…

嵌入式的应用领域

随着科技进步&#xff0c;嵌入式的出现&#xff0c;以及人们对生活质量&#xff0c;产品的智能化&#xff0c;成本的要求等&#xff0c;以及国家对与物联网、电子、科技的扶持&#xff0c;大量的电子产品都促使嵌入式的快速发展。使用嵌入式的产品如我们常用的手机、平板电脑、…

《2-数组》

数组 1.简介&#xff1a; 数组&#xff08;Array&#xff09;是一种固定长度的存储相同数据类型在连续内存空间中的数据结构 引出&#xff1a;[索引 &#xff08;Index&#xff09;]----元素在数组中的位置 2.初始化 写法&#xff1a;一般用到无初始值、给定初始值 在不给定…