您现在的位置是:首页 >学无止境 >Spring AOP 学习(动态代理、JdbcTemplate、Junit)网站首页学无止境
Spring AOP 学习(动态代理、JdbcTemplate、Junit)
动态代理
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;
}
@Override
public void eat(String foodName) {
System.out.println(name+"正在吃"+foodName);
}
@Override
public void drink( ) {
System.out.println(name+"正在喝茶");
}
}
class Student implements Dinner{
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void eat(String foodName) {
System.out.println(name+"正在食堂吃"+foodName);
}
@Override
public void drink( ) {
System.out.println(name+"正在喝可乐");
}
}
在不修改原有代码,或者没有办法修改原有代码的情况下,增强对象功能,使用代理对象代替原来的对象去完成功能,进而达到拓展功能的目的
JDK Proxy 动态代理是面向接口的动态代理,一定要有接口和实现类的存在,代理对象增强的是实现类实现接口时重写的方法
生成的代理对象只能转换成接口,不能转换成被代理类(上面只能是Dinner,不能是Person或Student)
代理对象只能增强接口中定义的方法, 实现类中其他和接口无关的方法是无法增强的
代理对象只能读取到接口中方法上的注解,不能读取到实现类方法上的注解
cglib动态代理
面向父类的,和接口没有直接关系
不仅仅可以增强接口中定义的方法,还可以增强一个类的其他方法
可以读取父类中方法上的所有注解
public class Test1 {
@Test
public void testCglib(){
Person person =new Person();
// 1 获得一个Enhancer对象
Enhancer enhancer=new Enhancer();
// 2 设置父类字节码
enhancer.setSuperclass(person.getClass());
// 3 获取MethodIntercepter对象 用于定义增强规则
MethodInterceptor methodInterceptor=new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
/*Object o, 生成之后的代理对象 personProxy
Method 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 设置methodInterceptor
enhancer.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 {
}