JavaEE——Spring学习笔记03【AOP开发】
——学习笔记01【Ioc开发的模式】
——学习笔记02【和的整合】
——学习笔记03【AOP开发】
——学习笔记04【的事务管理】
——学习笔记05【的逆行工程】
——学习笔记06【Maven创建Web工程】
目录
六、的AOP开发
1. AOP的概念
2.1 JDK动态代理
2.2 Cglib动态代理
3. AOP的编程的好处
4. AOP的编程相关术语
5. AOP的开发模式
5.1 Aop的Xml开发方式
5.2 Aop的注解开发方式
六、的AOP开发 1. AOP的概念
AOP的英文全程为 ,叫做面向切面编程,主要是在运行期间动态实现在不修改源码的情况下给程序及进行功能增强。针对目标功能进行扩展或是增强,通俗来说就是不修改源码,让目标获得它本身没有用的新功能。其实就是OOP编程的一个补充,降低了代码的耦合性。
AOP开发的底层是通过动态代理来完成,动态代理分为JDK动态代理和Cglib动态代理。
JDK动态代理:依赖于一个接口
Cglib动态代理:依赖一个类
2. 动态代理(了解) 2.1 JDK动态代理
只能对实现了接口的类产生代理。
实现的步骤:
1)业务的接口和实现类
/** 用户的接口* */
public interface UserDao {public void save();public void update();public void find();public void delete();
}
/** 用户接口实现类* */
public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println("保存用户信息");}@Overridepublic void update() {System.out.println("更改用户信息");}@Overridepublic void find() {System.out.println("查询用户信息");}@Overridepublic void delete() {System.out.println("删除用户信息");}
}
2)JDK动态代理的类
/** JDK动态代理的类:实现Invocation Handler* */
public class JdkProxy implements InvocationHandler {//将被增强的对象传递进来(目标类)private UserDao userDao;public JdkProxy(UserDao userDao) {this.userDao = userDao;}//产生UserDao代理的方法public UserDao createProxy() {/** 第一个参数:UserDao类的加载器* 第二个参数:要实现的接口* */UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(), this);return userDaoProxy;}/* 方法功能增强* 第一个参数: 代理对象* 第二个参数: 真正执行的方法* 第三个参数: 是方法的参数* */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if ("save".equals(method.getName())) {//增强System.out.println("权限校验==========");return method.invoke(userDao, args);}return method.invoke(userDao, args);}
}
3)测试方法
/** 用户方法增强的测试类* */
public class UserDaoTest {public static void main(String[] args) {//创建一个UserDao的实现类对象UserDao userDao = new UserDaoImpl();//创建代理的对象UserDao proxy = new JdkProxy(userDao).createProxy();proxy.save();proxy.update();proxy.find();proxy.delete();}
}
2.2 Cglib动态代理
1)针对类而言:引入第三代理的jar包
cglib cglib 2.1_3
2)目标类
/** 客户信息的类* */
public class CustomerService {public void save() {System.out.println("保存用户信息");}public void update() {System.out.println("更改用户信息");}public void find() {System.out.println("查询用户信息");}public void delete() {System.out.println("删除用户信息");}
}
3)Cglib代理的类
/** CglibProxy动态代理的类:实现MethodInterceptor* */
public class CglibProxy implements MethodInterceptor {//给出要代理的对象private CustomerService customerService;public CglibProxy(CustomerService customerService) {this.customerService = customerService;}/** 使用Cglib产生代理的方法* */public CustomerService createProxy() {//1.创建Cglib的核心类的对象Enhancer enhancer = new Enhancer();//2.设置父类enhancer.setSuperclass(customerService.getClass());//3.设置回滚enhancer.setCallback(this);//4.创建代理的对象CustomerService proxy = (CustomerService) enhancer.create();return proxy;}/** 功能增强* */@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//判断方法if ("delete".equals(method.getName())) {//增强System.out.println("日志信息==========");return methodProxy.invokeSuper(proxy, args);}return methodProxy.invokeSuper(proxy, args);}
}
4)测试的类
/** 客户信息测试类* */
public class CustomerServiceTest {public static void main(String[] args) {//创建一个目标类CustomerService customerService = new CustomerService();//代理过程CustomerService proxy = new CglibProxy(customerService).createProxy();proxy.save();proxy.update();proxy.find();proxy.delete();}
}
3. AOP的编程的好处
1.减少重复
2.专注于业务功能增强的实现,就是oop的补充。
3.解模块之间的耦合性
4.实现了事务的功能管理
5.。。。。。
4. AOP的编程相关术语
1)切面:
切面指的是一个辅助类,实际上就是对业务逻辑的一种增强管理类。
2)连接点:
连接点是指可以被切面植入的具体的方法,通常情况就是业务中的方法均可以作为连接点。
3)切入点:
切入点(切点)声明一个或多个连接点的集合,一般我们理解为真正被增强的那个方法或方法的集合。
4)目标对象:
目标对象就是指被增强的对象,即包含业务逻辑的类的对象
5)通知:
通知定义了增强代码切入到目标代码的时间点(可前可后进行增强),是目标方法执行前还是执行后等,通知的类型不同,导致切入的时间点就不一样了。
6)织入:
织入就是增强添加对目标类具体连接点的过程。
5. AOP的开发模式 5.1 Aop的Xml开发方式
本身有自己对AOP开发的实现,但是开发者发现了这个过程太过于繁琐了,所以在实际开发中我们使用的是技术实现对AOP的开发,其实就是简化本身的开发。其实现方式简洁、方便管理,而且还支持注解开发。所以又将作为AOP实现引入到自己框架中。
log4j.properties日志配置
log4j.rootLogger = debug,stdout,D,Elog4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%nlog4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = D://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%nlog4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =D://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
5.1.1 前置通知:
1)引入的依赖jar包
junit junit 4.13.2 test org.springframework spring-context 5.2.7.RELEASE org.springframework spring-aspects 5.2.7.RELEASE
2)配置切面(就是业务增强的那个类)
/** 账户类的切面:其实就是Account业务增强的类* */
public class AccountAspect {/** 验证账户的合法性* */public void validAuth() {System.out.println("验证账户是否合法!");}/** 检查余额是否足够* */public void validMoney() {System.out.println("检查余额是否充足!");}/** 生成对账单* */public void generateStatement() {System.out.println("生产对账单!");}/** 发送短信* */public void sendMsg() {System.out.println("发送短信!");}/** 后置通知:取完钱后查看余额的方法* */public void getYuMoney(Object result) {System.out.println("账户余额还有:" + result);}/** 利用环绕通知检查余额是否足够(前置)和生成对账单(后置)* */public Object validMoneyAndGenerateStatement(ProceedingJoinPoint point) throws Throwable {Object obj = null;//前置通知: 检查余额是否充足validMoney();obj = point.proceed(); //调用切点的方法//后置通知: 生成对账单generateStatement();return obj;}/** 异常通知* */public void exception(Exception e) {System.out.println("目标方法执行出错" + e.getMessage());}/** 最终的通知* */public void after() {System.out.println("不管有无异常,我都进行通知");}
}
3) 配置被切面(就是配置目标类)
/** 账号类* */
public class Account {int money = 1000;//取钱/*public void out() {money -= 100; //取出100元System.out.println("已扣除100元!");}*///取钱public int out() {money -= 100; //取出100元System.out.println("已扣除100元!");int num = 100 / 0; //设置异常return money;}//存钱public void in() {money += 100; //存储100元System.out.println("已存储100元!");}//转账public void transfer() {System.out.println("转账人民币!");}//开户public void open() {System.out.println("开通账户!");}//销户public void close() {System.out.println("销毁账户!");}
}
4)配值目标类和切面类的关系-aop.xml
容器管理
5)测试类
/** 测试账户的类* */
public class AccountTest {@Testpublic void testAccount() {//1.创建Spring容器的对象ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-aop.xml");//2.获取容器中Account对象Account account = (Account) applicationContext.getBean("account");//取钱account.out();}
}
5.1.2 后置通知:after-
在目标方法执行后进行通知
1)以取钱后显示余额为案例,目标类中的方法
//取钱public int out() {money -= 100; //取出100元System.out.println("已扣除100元!");return money;}
2)切面类中的方法
/** 后置通知:取完钱后查看余额的方法* */public void getYuMoney(Object result) {System.out.println("账户余额还有:" + result);}
3)容器中的配置
5.1.3 环绕通知:aroud
在目标方法执行前和执行后都进行通知,修改切面类中的方法!
1)切面类中的方法
/** 利用环绕通知检查余额是否足够(前置)和生成对账单(后置)* */public Object validMoneyAndGenerateStatement(ProceedingJoinPoint point) throws Throwable {Object obj = null;//前置通知: 检查余额是否充足validMoney();obj = point.proceed(); //调用切点的方法//后置通知: 生成对账单generateStatement();return obj;}
2)容器中的配置
3)测试类不变
/** 测试账户的类* */
public class AccountTest {@Testpublic void testAccount() {//1.创建Spring容器的对象ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-aop.xml");//2.获取容器中Account对象Account account = (Account) applicationContext.getBean("account");//取钱account.out();}
}
5.1.4 异常通知:after-
1)目标类中的方法
//取钱public int out() {money -= 100; //取出100元System.out.println("已扣除100元!");int num = 100 / 0; //设置异常return money;}
2)切面类中的方法
/** 异常通知* */public void exception(Exception e) {System.out.println("目标方法执行出错" + e.getMessage());}
3)容器中的配置
4)测试类不变
5.1.5 最终通知:after
1)切面类中的方法
/** 最终的通知* */public void after() {System.out.println("不管有无异常,我都进行通知");}
2)容器文件中的配置
3)测试类不变
AOP的总结:
1. 切什么?用什么切?
找切面与被切面,被切面是业务,切面是扩展业务
2. 从哪切?
找切点
3. 什么时间切?
定义通知类型(前置,后置,环绕,异常,最终)
5.2 Aop的注解开发方式
无论是xml的aop开发。还是注解的aopo开发,都可以实现方法增强的功能。
5.2.1 设置注解的包扫描器
5.2.2 AOP注解在使用前要先开启
5.2.3 目标类
/*** 账户类:被切面类*/@Component
public class Account {int money = 1000;//取钱
/* public void out(){money -=100;//取出100System.out.println("已扣除100元!");}*/public int out() {money -= 100;//取出100System.out.println("已扣除100元!");//int num = 100/0;//设置异常return money;}//存钱public void in() {money += 100;//存储100System.out.println("已存储100元!");}//转账public void transfer() {System.out.println("转账人民币!");}//开户public void open() {System.out.println("开通账户!");}//销户public void close() {System.out.println("账户销户!");}
}
5.2.4 切面类
/*** 账户类的切面:其实就是Account业务增强的类*/
@Component
@Aspect
public class AccountAspect {/*** 验证账户的合法性:前置通知1*/@Before("execution(public int com.suke.pojo.Account.out())")public void validAuth() {System.out.println("验证账户是否合法!");}/*** 检查余额是否足够:前置通知2*/@Before("execution(public int com.suke.pojo.Account.out())")public void validMoney() {System.out.println("检查余额是否充足!");}/*** 生成对账单*/@AfterReturning("execution(public int com.suke.pojo.Account.out())")public void generateStatement() {System.out.println("生成对账单!");}/*** 发送短信*/@AfterReturning("execution(public int com.suke.pojo.Account.out())")public void sendMsg() {System.out.println("发送短信!");}/*** 后置通知:取完钱后查看余额的方法*/
// @AfterReturning(value = "execution(public int com.suke.pojo.Account.out())",returning = "result")@AfterReturning(value = "accountPoint()", returning = "result")public void getYuMoney(Object result) {System.out.println("账户余额还有:" + result);}/*** 利用环绕通知检查余额是否足够(前置)和生成对账单(后置)*/@Around("execution(public int com.suke.pojo.Account.out())")public Object vaildMoneyAndGenerateStatement(ProceedingJoinPoint ponint) throws Throwable {Object obj = null;//前置通知:检查余额是否充足validMoney();obj = ponint.proceed(); //调用切点的方法//后置通知:生成对账单generateStatement();return obj;}/*** 异常通知*/
// @AfterThrowing(value = "execution(public int com.suke.pojo.Account.out())", throwing = "e")@AfterThrowing(value = "accountPoint()", throwing = "e")public void exception(Exception e) {System.out.println("目标方法执行出错!" + e.getMessage());}/*** 最终的通知*/
// @After("execution(public int com.suke.pojo.Account.out())")@After(value = "accountPoint()")public void after() {System.out.println("不管有无异常我都进行通知!");}/*** 配置一个切点*/@Pointcut(value = "execution(public int com.suke.pojo.Account.out())")public void accountPoint() {}
}
5.2.4 测试类
/*** 测试账户的类*/
public class AccountTest {@Testpublic void testAccount(){//1、创建Spring的容器对象ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-aop.xml");//2、获取容器中Account对象Account account = (Account) applicationContext.getBean("account");//取钱account.out();}
}