一:帝国之军-AOP
1:案例分析
事务案例图:
上诉问题:在我们的业务层中每一个业务方法都的处理事务(频繁的try-cache),在设计上存在两个很严重问题:
1):责任不分离,业务方法只需要关系如何完成业务功能,不需要去关心事务管理/日志管理/权限管理等.
2):代码结构重复,在开发中不要重复代码,重复就意味着维护成本增大.
房租租赁案例:
2.静态代理
代理模式:客户端直接使用的都是代理对象,不知道真实对象是谁,此时代理对象可以在客户端和真是对象之间起到中介的作用
1:代理对象完全包含真是对象,客户端使用的都是代理对象的方法,和真是对象没有直接关系;
2:代理模式的职责:把不是真是对象该做的事情从真实对象上撇开--职责清晰;
静态代理:在程序运行前就已经存在代理类的字节码文件,代理对象和真是对象的关系在运行前就确定了.
静态代理:
静态代理的优缺点:
//静态代理类
public class EmployeeServiceProxy implements IEmployeeService{
private IEmployeeService target;//真是对象/委托对象
private TransactionManager txManager;//事务管理器
public void setTarget(IEmployeeService target) {
this.target = target;
}
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
public void save(Employee emp) {
txManager.begin();
try {
target.save(emp);
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
}
public void update(Employee emp) {
txManager.begin();
try {
target.update(emp);
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
}
}
App
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class App {
@Autowired
//@Qualifier("employeeServiceProxy")
//因为代理类实现于接口,而接口还有一个imp类,xml配置文件的时候必须要标明,可以使用Qualifier标注,也可以跟下面一样内部<bean>表示
private IEmployeeService service;
@Test
public void testSave() throws Exception {
System.out.println(service.getClass());//查看对象的真实类型
service.save(new Employee());
}
@Test
public void testUpdate() throws Exception {
service.update(new Employee());
}
}
App-Context.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"
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
" >
<bean id="employeeDAO" class="cn.wolfcode.dao.imp.IEmployeeDAOImpl"/>
<bean id="transactionManager" class="cn.wolfcode.tx.TransactionManager"/>
<!-- 代理对象 -->
<bean id="employeeServiceProxy" class="cn.wolfcode.proxy.EmployeeServiceProxy">
<property name="txManager" ref="transactionManager"/>
<property name="target" >
<bean class="cn.wolfcode.service.IEmployeeServiceImpl">
<property name="dao" ref="employeeDAO"></property>
</bean>
</property>
</bean>
</beans>
3.动态代理
动态代理:动态代理是在程勋运行期间有JVM通过反射等机制动态的生成的,所以不存在代理类的字节码文件,代理
对象和真实对象的关系是在程序运行时期才确定的.
如何实现动态代理:
1):针对有接口:使用JDK动态代理
2):针对无接口:使用CGLIB或Javassist组件
①JDK动态代理
domain
public class Employee {
}
dao
public interface IEmployeeDAO {
void save(Employee emp);
void update(Employee emp);
}
impl
public class IEmployeeDAOImpl implements IEmployeeDAO{
public void save(Employee emp) {
System.out.println("保存员工");
System.out.println("保存成功");
}
public void update(Employee emp) {
System.out.println("修改员工");
throw new RuntimeException("故意错误");
}
}
service
public interface IEmployeeService {
void save(Employee emp);
void update(Employee emp);
}
impl
public class IEmployeeServiceImpl implements IEmployeeService{
private IEmployeeDAO dao;
public void setDao(IEmployeeDAO dao) {
this.dao = dao;
}
public void save(Employee emp) {
dao.save(emp);
}
public void update(Employee emp) {
dao.update(emp);
}
}
tx
//模拟事务管理器:
public class TransactionManager {
public void begin(){
System.out.println("开启事务");
}
public void commit(){
System.out.println("提交事务");
}
public void rollback(){
System.out.println("回滚事务");
}
}
//事务的增强操作
@SuppressWarnings("all")
public class TransactionManagerAdvice implements InvocationHandler{
private Object targer;//真实对象(对谁做增强)
private TransactionManager txManager;//事务管理器(模拟)
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
public void setTarger(Object targer) {
this.targer = targer;
}
//创建一个代理对象
public <T> T getProxyObject(){
return (T)Proxy.newProxyInstance(targer.getClass().getClassLoader(),//类加载器,一般跟上真实对象的类加载器S
targer.getClass().getInterfaces(),//真实对象实现的接口(JDK动态代理必须要求真是对象有接口)
this);//如何做事务增强的对象
}
//如何为真实对象的方法做增强的具体操作
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().startsWith("get")|| method.getName().startsWith("list")){
return method.invoke(targer, args);//调用真实对象的方法
}
Object obj = null;
txManager.begin();
try {
//-----------------------------------------------------
obj = method.invoke(targer, args);//调用真实对象的方法
//-----------------------------------------------------
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
return obj;
}
}
App
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class App {
@Autowired
private TransactionManagerAdvice advice;
//代理对象:com.sun.proxy.$Proxy19
@Test
public void testSave() throws Exception {
//获取代理对象
IEmployeeService proxy = advice.getProxyObject();
proxy.save(new Employee());
}
@Test
public void testUpdate() throws Exception {
//获取代理对象
IEmployeeService proxy = advice.getProxyObject();
proxy.update(new Employee());
}
}
App-Context.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"
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
" >
<bean id="employeeDAO" class="cn.wolfcode.dao.imp.IEmployeeDAOImpl"/>
<bean id="transactionManager" class="cn.wolfcode.tx.TransactionManager"/>
<bean id="employeeService" class="cn.wolfcode.service.impl.IEmployeeServiceImpl">
<property name="dao" ref="employeeDAO"></property>
</bean>
<!-- 代理对象 -->
<!-- 配置一个事务增强的类 -->
<bean id="transactionManagerAdvice" class="cn.wolfcode.tx.TransactionManagerAdvice">
<property name="targer" ref="employeeService"></property>
<property name="txManager" ref="transactionManager" />
</bean>
</beans>
②:CGLIB动态代理
domain
public class Employee {
}
dao
public interface IEmployeeDAO {
void save(Employee emp);
void update(Employee emp);
}
impl
public class IEmployeeDAOImpl implements IEmployeeDAO{
public void save(Employee emp) {
System.out.println("保存员工");
System.out.println("保存成功");
}
public void update(Employee emp) {
System.out.println("修改员工");
throw new RuntimeException("故意错误");
}
}
service.impl
public class EmployeeServiceImpl{
private IEmployeeDAO dao;
public void setDao(IEmployeeDAO dao) {
this.dao = dao;
}
public void save(Employee emp) {
dao.save(emp);
}
public void update(Employee emp) {
dao.update(emp);
}
}
tx
public class TransactionManager {
public void begin(){
System.out.println("开启事务");
}
public void commit(){
System.out.println("提交事务");
}
public void rollback(){
System.out.println("回滚事务");
}
}
@SuppressWarnings("all")
public class TransactionManagerAdvice implements org.springframework.cglib.proxy.InvocationHandler{
private Object target;//真实对象(对谁做增强)
private TransactionManager tx;//事务管理器(模拟)
public void setTarget(Object target) {
this.target = target;
}
public void setTx(TransactionManager tx) {
this.tx = tx;
}
//创建一个代理对象
public <T> T getProxyObject(){
Enhancer enhencer = new Enhancer();
enhencer.setSuperclass(target.getClass());//将继承与哪一个类,去做增强
enhencer.setCallback(this);//创建增强的对象
return (T) enhencer.create();//创建代理对象
}
//如何为真实对象的方法做增强的具体操作
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
tx.begin();
try {
obj = method.invoke(target, args);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}
return obj;
}
}
App
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class App {
@Autowired
private TransactionManagerAdvice advice;
//代理对象:com.sun.proxy.$Proxy19
//CGLIB代理对象:class cn.wolfcode.service.impl.IEmployeeServiceImpl$$EnhancerByCGLIB$$8db50f30
@Test
public void testSave() throws Exception {
EmployeeServiceImpl proxy = advice.getProxyObject();
proxy.save(new Employee());
}
@Test
public void testUpdate() throws Exception {
EmployeeServiceImpl proxy = advice.getProxyObject();
proxy.update(new Employee());
}
}
App-Context.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"
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
" >
<bean id="employeeDAO" class="cn.wolfcode.dao.imp.IEmployeeDAOImpl"/>
<bean id="employeeService" class="cn.wolfcode.service.impl.EmployeeServiceImpl">
<property name="dao" ref="employeeDAO"></property>
</bean>
<bean id="transactionManager" class="cn.wolfcode.tx.TransactionManager"></bean>
<!-- 代理对象 -->
<!-- 配置一个事务增强的类 -->
<bean id="transactionManagerAdvice" class="cn.wolfcode.tx.TransactionManagerAdvice">
<property name="target" ref="employeeService"></property>
<property name="tx" ref="transactionManager"></property>
</bean>
</beans>
③:拦截器的原理和日志记录
拦截器跟Filter过滤器很相似:
Filter就是对请求和响应做拦截.
Filter: WEB领域的概念,只能针对请求和响应做增强,离不开servlet-api.jar
Integerceptor: 整个Java领域的概念,不仅可以运用到service层,还可以用到Web层.
domain
public class Employee {
}
dao
public interface IEmployeeDAO {
void save(Employee emp);
void update(Employee emp);
}
imp
public class IEmployeeDAOImpl implements IEmployeeDAO{
public void save(Employee emp) {
System.out.println("保存员工");
System.out.println("保存成功");
}
public void update(Employee emp) {
System.out.println("修改员工");
throw new RuntimeException("故意错误");
}
}
service
public class IEmployeeServiceImpl{
private IEmployeeDAO dao;
public void setDao(IEmployeeDAO dao) {
this.dao = dao;
}
public void save(Employee emp) {
dao.save(emp);
}
public void update(Employee emp) {
dao.update(emp);
}
}
log
public class LogUtil {
public void writeLog(String className,String methodName){
System.out.println(new Date().toLocaleString()+"调用了"+className+"类中的"+methodName+"方法");
}
}
//日志增强
public class LogAdvice implements org.springframework.cglib.proxy.MethodInterceptor{
private Object target;//真实对象
private LogUtil logUtil;
public void setTarget(Object target) {
this.target = target;
}
public void setLogUtil(LogUtil logUtil) {
this.logUtil = logUtil;
}
//创建代理对象
public <T> T getProxyObject(){
/*Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return (T) enhancer.create();*/
return (T) Enhancer.create(target.getClass(),this);
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
logUtil.writeLog(method.getDeclaringClass().getName(),method.getName());
Object ret = method.invoke(target,args);//调用真实对象的方法
return ret;
}
}
App
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class App {
@Autowired
private LogAdvice advice;
//代理对象:com.sun.proxy.$Proxy19
//CGLIB代理对象:class cn.wolfcode.service.impl.IEmployeeServiceImpl$$EnhancerByCGLIB$$8db50f30
@Test
public void testSave() throws Exception {
IEmployeeServiceImpl proxy = advice.getProxyObject();
proxy.save(new Employee());
}
@Test
public void testUpdate() throws Exception {
IEmployeeServiceImpl proxy = advice.getProxyObject();
proxy.update(new Employee());
}
}
App-context.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"
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
" >
<bean id="employeeDAO" class="cn.wolfcode.dao.imp.IEmployeeDAOImpl"/>
<bean id="logUtil" class="cn.wolfcode.log.LogUtil"/>
<bean id="employeeService" class="cn.wolfcode.service.impl.IEmployeeServiceImpl">
<property name="dao" ref="employeeDAO"></property>
</bean>
<!-- 代理对象 -->
<!-- 配置一个事务增强的类 -->
<bean id="logAdvice" class="cn.wolfcode.log.LogAdvice">
<property name="target" ref="employeeService"></property>
<property name="logUtil" ref="logUtil" />
</bean>
</beans>
4.代理总结
JDK动态代理总结:
1:Java动态代理是使用java.lang.reflect包中的Proxy类与InvocationHandler接口这两个来完成的.
2:要使用JDK动态代理,委托必须要定义接口.
3:JDK动态代理将会拦截所有public的方法(因为只能调用接口中定义的方法),这样即使在接口中定义了方法,不用修改代码也会被拦截.
4:动态代理的最小单位是类(所有类中的方法都会被处理),如果只想拦截一部分方法,可以在invoke方法中对要执行的方法名进行判断.
CGLIB代理总结:
1:CGLIB可以生成委托类的子类,并重写父类非final修饰的方法.
2:要求类不能是final,要拦截的方法要是非final,非static, 非private的.
3:动态代理的最小单位是类(所有类中的方法都会被处理).
关于性能:
JDK动态代理是基于实现接口的,CGLIB和Javassit是基于继承委托类的.
从性能上考虑: Javassit > CGLIB > JDK
Struts2的拦截器和Hibemate延迟加载对象,采用的是Javassit的方式.
对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,也更符合面向接口编程规范.
若委托对象实现了接口,优先选用JDK动态代理.
若委托对象没有实现任何接口,使用Javassit和CGLIB动态代理.
5.AOP思想
1.AOP思想和重要术语
AOP(Aspect oritention Programming):
把一个个的横切关注点放到某个模块中去,称之为切面.那么每一个的切面都能影响业务的某一种功能,切面的目的就是功能增强,
如日志切面就是一个横切关注点,应用中许多方法需要做日志记录,只需要插入日志的切面即可.
Joinpoint: 连接点,被拦截到需要被增强的方法 WHERE 去哪里做增强操作
Pointcut: 切入点,需要为哪些包中的哪些类中的哪些方法.Joinpoint的集合. WHERE:去哪些地方做增强.
Advice: 增强(通知),当拦截到Joinpoint之后,在方法执行的某一个时机(when),做怎么样的增强操作(what).
Aspect: 切面 Pointcut + Advice 去哪些地方+在什么时候,做说明增强
Target: 目标对象,被代理的目标对象
Weaving: 织入,把 Advice 加到 Target 上之后,创建出Proxy对象的过程.
Proxy: 一个类被AOP织入增强后,产生的代理类.
方法执行的时机:
前置增强, 后置增强, 异常增强, 最终增强, 环绕增强
①:Pointcut表达式
AOP的规范本应该由SUN公司提出,但是被AOP联盟捷足先登,AOP联盟制定AOP规范,首先就要解决一个问题,怎么表示切入点,也就是在哪些方法上增强(where).
AspectJ是一个面向切面的框架,Aspect切入点语法如下(表示在哪些包下的哪些类的哪些方法上做切入增强):
翻译成中文:execution(<修饰符>? <返回类型> <声明类型>? <方法名>(<参数>) <异常>?)
举例 :public static Class java.Class.forName(String className)throws ClassNotFoundException
通配符:
* :匹配任何部分,只能表示一个单词
.. :可用于全限定名中和方法参数中,分别表示子包和0到N个参数
2.AOP开发
①:依赖
依赖的jar:
spring-aop-版本.RELEASE.jar
com.springsource.org-aopalliance-1.0.0.jar Spring5以后不需要拷贝,纳入了aop包中
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
②:AOP各种增强
before:前置增强 被增强方法执行之前执行 权限控制日志记录等
after-returning:后置增强 正常执行完毕后执行 提交事务/统计分析数据结果等
after-throwing:异常增强 被增强方法出现异常时执行 回滚事务/记录日志的日常信息
after:最终增强 无论是否有异常,最终都要执行的增强操作 释放资源等
around:环绕增强 可以自定义在被增强方法的什么时机执行(返回一个Object,参数ProceedingJoinPoint) 缓存,性能日志,权限,事务管理
获取被增强方法的信息,并可以传递给增强方法:
Spring AOP: Joinpoint类 连接点, 访问被增强方法的真是对象,代理对象,方法参数等等
可以做前置,后置,异常,最终增强方法的参数,第一个参数
ProceedingJoinpoint:是Joinipoint的子类,只用于环绕增强,作为一个参数,
还可以调用真实对象中被增强的方法:
如何获取增强方法里的异常信息, 增强方法里的参数JoinPoint,以及如何在环绕增强里面调用真实方法?
//模拟事务管理器:
public class TransactionManager {
public void begin(JoinPoint po){
System.out.println("代理对象: "+po.getThis().getClass());
System.out.println("目标对象: "+po.getTarget().getClass());
System.out.println("被增强方法参数: "+Arrays.toString(po.getArgs()));
System.out.println("当前连接点签名: "+po.getSignature());
System.out.println("当前连接点类型: "+po.getKind());
System.out.println("开启事务");
}
public void commit(JoinPoint po){
System.out.println("提交事务");
}
public void rollback(JoinPoint po,Throwable tx){
System.out.println("回滚事务"+" 方法的错误信息: "+tx.getMessage());
}
public void after(JoinPoint po){
System.out.println("释放资源");
}
public Object aroundMethod(ProceedingJoinPoint po){
Object ret = null;
System.out.println("开启事务");
try {
ret = po.proceed();//调用真实对象的方法,取的一个返回值,查询有返回值
System.out.println("提交事务");
} catch (Throwable e) {
System.out.println("回滚事务 错误信息:"+e.getMessage());
} finally {
System.out.println("释放资源");
}
return ret;
}
}
<!-- 1:what,做说明增强 -->
<bean id="txManager" class="cn.wolfcode.tx.TransactionManager" />
<aop:config proxy-target-class="false">
<aop:aspect ref="txManager"> <!-- 关联what -->
<!-- 2:where:在哪些语句中的哪些类中的哪些方法上做增强 -->
<aop:pointcut id="txPoint" expression="execution( * cn.wolfcode.service.*Service.*(..))"/>
<!-- 3:when:在方法执行的什么时机做增强 -->
<aop:before method="begin" pointcut-ref="txPoint"/>
<aop:after-returning method="commit" pointcut-ref="txPoint"/>
<aop:after-throwing method="rollback" pointcut-ref="txPoint" throwing="tx"/>
<aop:after method="after" pointcut-ref="txPoint"/>
<!-- <aop:around method="aroundMethod" pointcut-ref="txPoint"/> -->
</aop:aspect>
</aop:config>
③:使用注解开发AOP
@Component
@Aspect //配置一个切面
public class TransactionManager {
@Pointcut("execution( * cn.wolfcode.service.impl.*.*(..))")
public void txPoint(){
}
//@Before("txPoint()")
public void begin(JoinPoint po){
System.out.println("开启事务");
}
//@AfterReturning("txPoint()")
public void commit(JoinPoint po){
System.out.println("提交事务");
}
//@AfterThrowing(value="txPoint()",throwing="tx")
public void rollback(JoinPoint po,Throwable tx){
System.out.println("回滚事务"+" 方法的错误信息: "+tx.getMessage());
}
//@After("txPoint()")
public void after(JoinPoint po){
System.out.println("释放资源");
}
@Around("txPoint()")
public Object aroundMethod(ProceedingJoinPoint po){
Object ret = null;
System.out.println("开启事务");
try {
ret = po.proceed();//调用真实对象的方法,取的一个返回值,查询有返回值
System.out.println("提交事务");
} catch (Throwable e) {
System.out.println("回滚事务 错误信息:"+e.getMessage());
} finally {
System.out.println("释放资源");
}
return ret;
}
}
<!-- DI注解解析器 -->
<context:annotation-config/>
<!-- IoC注解解析器-->
<context:component-scan base-package="cn.wolfcode"/>
<!-- AOP注解解析器 -->
<!-- 当有接口的时候自动使用JDK代理, 没有接口的时候自动使用CGLIB代理 AOP的本质是动态代理 -->
<aop:aspectj-autoproxy proxy-target-class="false"/>