AOP介绍
AOP,既面向切面编程,可以说是OOP(面向对象编程)的补充和完善
面向切面是面向对象中的一种方式,在代码执行过程中,动态嵌入其他代码,叫做面向切面编程,常见使用场景: 日志 ; 事务; 数据库操作,等
面向切面编程的几个核心概念
概念 | 说明 |
---|---|
IOC/DI | 本质就是java反射+XML解析 |
AOP | 本质上就是java动态代理 |
切点 | 要添加代码的地方称作切点 |
切面 | 切点+通知 |
通知(增强) | 向切点插入的代码称为通知Advice |
连接点 | 切点的定义 |
AOP的实现方式
AOP介绍
面向切面编程,就是将交叉业务逻辑封装成切面,利用AOP的功能将切面织入到主业务逻辑中;所谓交叉业务逻辑是指,通用的,与业务逻辑无关的代码,如安全检查,事务,日志等;若不使用AOP,则会出现错误代码纠缠,既交叉业务逻辑与业务逻辑混合到一起;这样,会使业务逻辑变得混杂不清。
AOP术语介绍
通知类型 | 说明 |
---|---|
前置通知(MethodBeforeAdvice) | 目标方法执行之前调用 |
后置通知(AfterReturningAdvice) | 目标方法执行完成之后调用 |
环绕通知(MethodInterceptor) | 目标方法执行前后都会调用方法,且能增强结果 |
异常处理通知(ThrowsAdvice) | 目标方法出现异常调用 |
基于Schema-based方式实现
需要添加的jar包
前置通知
1.创建目标接口和实现类
package com.sxt.service;
/**
* service层
* @author Administrator
*
*/
public interface UserService {
public void say(String msg);
public String dosame(String msg);
}
package com.sxt.service.impl;
import com.sxt.service.UserService;
/**
* service实现类
* @author Administrator
*
*/
public class UserServiceImpl implements UserService{
@Override
public void say(String msg) {
System.out.println("say---"+msg+1/0);
}
@Override
public String dosame(String msg) {
System.out.println("dosame---"+msg);
return "hello";
}
}
2.创建切面类
package com.sxt.advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/**
* 前置通知
* @author Administrator
*
*/
public class Before implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置通知执行了..."+args[0]+" "+method.getName());
}
}
3.配置文件中配置
<?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-4.3.xsd">
<!-- 注册user实现类 -->
<bean class="com.sxt.service.impl.UserServiceImpl" id="userService"></bean>
<!-- 注册前置通知 -->
<bean class="com.sxt.advice.Before" id="before"></bean>
<!-- 注册代理类 -->
<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
<!-- 设置注入目标对象 -->
<property name="target" ref="userService"/>
<!-- 注册目标接口 -->
<property name="interfaces" value="com.sxt.service.UserService"/>
<!-- 注册前置通知 -->
<property name="interceptorNames">
<list>
<!-- 前置通知执行 -->
<value>before</value>
</list>
</property>
</bean>
</beans>
5.测试
package com.sxt;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.sxt.service.UserService;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
UserService bean = ac.getBean("proxyFactoryBean",UserService.class);
bean.say("hello");
System.out.println(bean.dosame("haha"));
}
}
后置通知
1.接口和之前一样
2.创建后置通知切面类
package com.sxt.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;
/**
* 后置通知
* @author Administrator
*
*/
@Component
public class After implements AfterReturningAdvice{
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置通知执行了"+returnValue);
}
}
3.配置文件配置,这里使用注解
<?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-4.3.xsd">
<!-- 开启扫描 -->
<context:component-scan base-package="com.sxt.advice"></context:component-scan>
<!-- 注册user实现类 -->
<bean class="com.sxt.service.impl.UserServiceImpl" id="userService"></bean>
<!-- 注册代理类 -->
<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
<!-- 设置注入目标对象 -->
<property name="target" ref="userService"/>
<!-- 注册目标接口 -->
<property name="interfaces" value="com.sxt.service.UserService"/>
<property name="interceptorNames">
<list>
<!-- 后置通知执行 -->
<value>after</value>
</list>
</property>
</bean>
</beans>
测试
环绕通知
1.创建环绕通知切面类
package com.sxt.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;
/**
* 环绕通知
* @author Administrator
*
*/
@Component
public class Method implements MethodInterceptor{
/**
* 可以增强目标对象的返回结果
*/
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("执行前");
Object proceed = arg0.proceed();
if(proceed != null){
proceed = proceed.toString().toUpperCase();
}
System.out.println("执行后");
return proceed;
}
}
2.配置文件中配置
<?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-4.3.xsd">
<!-- 开启扫描 -->
<context:component-scan base-package="com.sxt.advice"></context:component-scan>
<!-- 注册user实现类 -->
<bean class="com.sxt.service.impl.UserServiceImpl" id="userService"></bean>
<!-- 注册代理类 -->
<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
<!-- 设置注入目标对象 -->
<property name="target" ref="userService"/>
<!-- 注册目标接口 -->
<property name="interfaces" value="com.sxt.service.UserService"/>
<!-- 注册前置通知 -->
<property name="interceptorNames">
<list>
<!-- 环绕通知执行 -->
<value>method</value>
</list>
</property>
</bean>
</beans>
3.测试
异常通知
1.创建异常通知切面类
package com.sxt.advice;
import org.springframework.aop.ThrowsAdvice;
import org.springframework.stereotype.Component;
/**
* 异常通知
* @author Administrator
*
*/
@Component
public class Throw implements ThrowsAdvice{
/**
* 目标对象异常产生后处理的方法
* @param ex
*/
public void afterThrowing(Exception ex){
System.out.println("异常通知。。。"+ex.getMessage());
}
}
ThrowsAdvice接口没有定义方法,是个标志接口,在注释中有提示
2.配置文件配置
<?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-4.3.xsd">
<!-- 开启扫描 -->
<context:component-scan base-package="com.sxt.advice"></context:component-scan>
<!-- 注册user实现类 -->
<bean class="com.sxt.service.impl.UserServiceImpl" id="userService"></bean>
<!-- 注册前置通知 -->
<bean class="com.sxt.advice.Before" id="before"></bean>
<!-- 注册代理类 -->
<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
<!-- 设置注入目标对象 -->
<property name="target" ref="userService"/>
<!-- 注册目标接口 -->
<property name="interfaces" value="com.sxt.service.UserService"/>
<!-- 注册前置通知 -->
<property name="interceptorNames">
<list>
<!-- 前置通知执行 -->
<!-- <value>before</value> -->
<!-- 后置通知执行 -->
<!-- <value>after</value> -->
<!-- 环绕通知执行 -->
<!-- <value>method</value> -->
<!-- 异常通知执行 -->
<value>throw</value>
</list>
</property>
</bean>
</beans>
3.测试
基于aspectJ方式实现
对于AOP这种编程思想,很多框架都进行了实现。Spring就是其中之一,可以完成面向切面编程。然而,AspectJ也实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring又将AspectJ的对于AOP的实现也引入到了自己的框架中。在Spring中使用AOP开发时,一般使用AspectJ的实现方式
aspectJ中的通知类型
通知类型 | 说明 |
---|---|
前置通知 | 目标方法执行之前调用 |
后置通知 | 目标方法执行完成之后调用 |
环绕通知 | 目标方法执行前后都会调用方法,且能增强结果 |
异常处理通知 | 目标方法出现异常调用 |
最终通知 | 无论程序执行是否正常,该通知都会执行。类似于try…catch中finally代码块 |
AspectJ的切入点表达式
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution表达式中明显就是方法的签名。注意,表达式中加[ ]的部分表示可省略部分,各部分间用空格分开。在其中可以使用以下符号
符号 | 意义 |
---|---|
* | 0至多个字符 |
… | 方法参数中表示任意多个参数,用在包名后表示当前包及其子包路径 |
+ | 用在类名后当前类及其子类,用在接口后表示接口及其实现类 |
举例:
execution(public * (. .))
指定切入点为:任意公共方法。
execution( set (. .))
指定切入点为:任何一个以“set”开始的方法。
execution( com.xyz.service..(. .))
指定切入点为:定义在service包里的任意类的任意方法。
execution(* com.xyz.service. ..(. .))
指定切入点为:定义在service包或者子包里的任意类的任意方法。“…”出现在类名中时,
后面必须跟“”,表示包、子包下的所有类。
execution( .service..(. .))
指定只有一级包下的serivce子包下所有类(接口)中的所有方法为切入点
execution( . .service..*(. .))
指定所有包下的serivce子包下所有类(接口)中的所有方法为切入点
AspectJ对于AOP的实现有两种方式
引入jar包
注解方式
1.接口实现类同上
2.创建切面类
package com.sxt.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class Advice {
/**
* 前置通知
*/
@Before(value="execution(* com.sxt.service.impl.*.*(..))")
public void before(){
//System.out.println("前置通知----");
}
/**
* 后置通知
* returning:指定接收返回结果的变量
*/
@AfterReturning(value="execution(* com.sxt..*(..))",returning="msg")
public void afterReturing(Object msg){
//System.out.println("后置通知:"+msg);
}
/**
* 异常通知
*/
@AfterThrowing(value="execution(* com.sxt..*(..))",throwing="ex")
public void throwse(Exception ex){
//System.out.println("异常通知"+ex);
}
/**
* 环绕通知
* @throws Throwable
*/
@Around(value="execution(* com.sxt..*(..))")
public Object around(ProceedingJoinPoint p) throws Throwable{
System.out.println("环绕前");
Object proceed = p.proceed();
if(proceed != null){
proceed = proceed.toString().toUpperCase();
}
System.out.println("环绕后");
return proceed;
}
/**
* 最终通知
*/
@After(value="execution(* com.sxt..*(..))")
public void after(){
System.out.println("最终通知");
}
}
3.配置文件配置
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 开启扫描 -->
<context:component-scan base-package="com.sxt.*"></context:component-scan>
<!-- AspectJ 开启注解的方式 -->
<aop:aspectj-autoproxy/>
</beans>
效果图同上
XML实现
接口实现类还是使用开始的案例
切面类
package com.sxt.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
public class Advice {
/**
* 前置通知
*/
public void before(){
//System.out.println("前置通知----");
}
/**
* 后置通知
* returning:指定接收返回结果的变量
*/
public void afterReturing(Object msg){
//System.out.println("后置通知:"+msg);
}
/**
* 异常通知
*/
public void throwse(Exception ex){
//System.out.println("异常通知"+ex);
}
/**
* 环绕通知
* @throws Throwable
*/
public Object around(ProceedingJoinPoint p) throws Throwable{
System.out.println("环绕前");
Object proceed = p.proceed();
if(proceed != null){
proceed = proceed.toString().toUpperCase();
}
System.out.println("环绕后");
return proceed;
}
/**
* 最终通知
*/
public void after(){
System.out.println("最终通知");
}
}
配置文件修改
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 注册user实现类 -->
<bean class="com.sxt.service.impl.UserServiceImpl"></bean>
<!-- 注册advice -->
<bean class="com.sxt.advice.Advice" id="advice"></bean>
<!-- AspectJ 开启注解的方式 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.sxt.service.impl.*.*(..))" id="pointcut"/>
<!-- 配置切面类 -->
<aop:aspect ref="advice">
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
<aop:after method=“after” pointcut-ref=“pointcut”/>将after改为切面类中的其他通知的方法名即可实现,这里就不一一例举了
JdbcTemplate
JdbcTemplate就是采用AOP思想,将Jdbc操作中的模板代码全部简化,开发者只需要完成最最核心的SQL以及结果的检索。
使用步骤:
导入jar包
1.创建user对象
package com.sxt.bean;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
}
}
2.创建接口和方法
package com.sxt.dao;
public interface UserDao {
public void add();
public void update();
public void delete();
public void query();
}
3.创建接口实现类
package com.sxt.dao.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.stereotype.Repository;
import com.sxt.bean.User;
import com.sxt.dao.UserDao;
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate template;
@Override
public void add() {
String sql = "insert t_user(username,password)values(?,?)";
int i = template.update(sql,"aa","11");
System.out.println("影响一行"+i);
}
@Override
public void update() {
String sql = "update t_user set username=?,password=? where id=?";
int i = template.update(sql,"bb","22",2);
System.out.println("影响一行"+i);
}
@Override
public void delete() {
String sql = "delete from t_user where id=?";
int i = template.update(sql,2);
System.out.println("影响一行"+i);
}
@Override
public void query() {
String sql = "select * from t_user";
List<User> query = template.query(sql, new BeanPropertyRowMapper<User>(User.class));
for (User user : query) {
System.out.println(user);
}
}
}
4.配置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"
xmlns:aop="http://www.springframework.org/schema/aop"
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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 开启注解 -->
<context:component-scan base-package="com.sxt.dao.*"></context:component-scan>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
<!-- 设置注入 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver "/>
<property name="url" value="jdbc:mysql://localhost:3306/pms?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>
</beans>
测试
package com.sxt;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.sxt.dao.UserDao;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
UserDao bean = ac.getBean(UserDao.class);
bean.add();
}
}