代理
- 静态代理基于继承实现
- 动态代理是基于接口实现
业务层每次实现转账都需要执行,可以把他们拿出来当成一个切面,自己写出一个代理类,让业务层只执行业务的逻辑,重复的代码代理类来完成,然后调用代理类来执行。
代理类
java
package com.qcby.utils;
import com.qcby.service.AccountService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//传入目标对象,生成该对象的代理对象,返回。对目标对象的方法进行增强
public class ProxyUtils {
//获取代理对象,返回,增强目标对象的方法
public static Object getProxy(final AccountService accountService){
//使用jdk动态dialing生成代理对象
Object proxy = Proxy.newProxyInstance(ProxyUtils.class.getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
//调用代理对象的方法,invoke方法就会去执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目标对象的方法
Object result = null;
try {
//开启事务
TxUtils.startTransaction();
//目标对象的方法进行增强,作为结果返回
result = method.invoke(accountService,args);
//事务提交
TxUtils.commit();
}catch (Exception e){
e.printStackTrace();
//事务回滚
TxUtils.rollback();
}finally {
//资源关闭
TxUtils.close();
}
return result;
}
});
return proxy;
}
}
测试
java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Demo2 {
@Autowired
private AccountService accountService;
@Test
public void run1(){
Account account1 = new Account();
account1.setName("aaa");
Account account2 = new Account();
account2.setName("bbb");
//创建代理对象
AccountService proxy = (AccountService) ProxyUtils.getProxy(accountService);
proxy.saveAll(account1,account2);
}
}
AOP
配置文件形式:(IOC也是用的配置文件形式)
配置文件
java
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--将目标类配置到spring中-->
<bean id="userService" class="com.qcby.demo1.UserServiceImpl"/>
<!--将切面类配置到spring中-->
<bean id="myXmlAspect" class="com.qcby.demo1.MyXmlAspect"/>
<!--配置AOP的增强-->
<aop:config>
<!--配置切面 = 通知+切入点组成-->
<aop:aspect ref="myXmlAspect">
<!--通用写法-->
<!--<aop:before method="log" pointcut="execution(public * com.qcby.*.*ServiceImpl.*(..))"/>-->
<!--前置通知:无论方法成功与否都执行-->
<!--<aop:before method="log" pointcut="execution(public void com.qcby.demo1.UserServiceImpl.save())"/>-->
<!--最终通知:失败成功都执行-->
<!--<aop:after method="log" pointcut="execution(public void com.qcby.demo1.UserServiceImpl.save())"/>-->
<!--后置通知:方法成功执行之后执行-->
<!--<aop:after-returning method="log" pointcut="execution(public * com.qcby.*.*ServiceImpl.*(..))"/>-->
<!--异常通知:有异常才执行-->
<!--<aop:after-throwing method="log" pointcut="execution(public void com.qcby.demo1.UserServiceImpl.save())"/>-->
<!--环绕通知:目标方法执行前后都执行 执行方法成功与否对执行前的增强不影响(方法执行不成功也执行前置的)-->
<aop:around method="aroundLog" pointcut="execution(public * com.qcby.demo1.*ServiceImpl.*(..))"/>
</aop:aspect>
</aop:config>
</beans>
切面类
java
package com.qcby.demo1;
import org.aspectj.lang.ProceedingJoinPoint;
/*定义切面类 = 切入点(表达式)+通知*/
//在配置文件里配置成切面类=增强的方法(通知)+需要增强的方法(切入点)
public class MyXmlAspect {
/*通知*/
public void log(){
//发送手机短信
//发送邮件、记录日志、事务管理
System.out.println("增强的方法执行了....");
}
public void log1(){
//发送手机短信
//发送邮件、记录日志、事务管理
System.out.println("前置增强的方法执行了....");
}
public void log2(){
//发送手机短信
//发送邮件、记录日志、事务管理
System.out.println("后置增强的方法执行了....");
}
/*环绕通知*/
public void aroundLog(ProceedingJoinPoint proceedingJoinPoint){
try {
log1();
proceedingJoinPoint.proceed();
log2();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
测试类
java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Demo1 {
@Autowired
private UserService userService;
/*测试*/
@Test
public void run1(){
userService.save();
}
}
半注解方式
切面类=通知+切入点(现在的切面类已经在通知上添加了切入点)
java
package com.qcby.demo2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component // 把该类交给 IOC 去管理
@Aspect // 声明是切面类 == <aop:aspect ref="myXmlAspect">
public class MyAnnoAspect {
//@Before(value = "execution(public * com.qcby.*.*ServiceImpl.*(..))")
public void log1(){
System.out.println("前置通知增强的方法执行...");
}
//@AfterReturning(value = "execution(public * com.qcby.*.*ServiceImpl.*(..))")
public void log2(){
System.out.println("后置通知增强的方法执行...");
}
//@After(value = "execution(public * com.qcby.*.*ServiceImpl.*(..))")
public void log3(){
System.out.println("最终通知增强的方法执行...");
}
//@AfterThrowing(value = "execution(public * com.qcby.*.*ServiceImpl.*(..))")
public void log4(){
System.out.println("异常通知增强的方法执行...");
}
@Around(value = "execution(public * com.qcby.*.*ServiceImpl.*(..))")
public void log5(ProceedingJoinPoint proceedingJoinPoint){
try {
log1();
proceedingJoinPoint.proceed();
log2();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
配置文件
java
<!--配置文件中开启自动代理-->
<aop:aspectj-autoproxy/>
<!--开启注解扫描-->
<context:component-scan base-package="com.qcby" />
测试类
java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Demo2 {
@Autowired
private AccountService accountService;
@Test
public void run1(){
accountService.save();
}
}
纯注解形式
配置类
java
@Configuration // 配置类
@ComponentScan(value = "com.qcby") // 扫描包
@EnableAspectJAutoProxy // 开启自动代理 == <aop:aspectj-autoproxy/>
public class SpringConfig {
}
切面类
与半注解形式一样
测试类
java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class Demo2 {
@Autowired
private AccountService accountService;
@Test
public void run1(){
accountService.save();
}
}
出现异常时:前置方法,最终方法,异常方法都会执行