Spring--AOP 面向切面编程

Spring–AOP 面向切面编程

一.AOP介绍

为什么使用AOP?

  • java是oop面向对象的语言,OOP引入封装、继承和多态等概念来建立一种对象层次结构,也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码一般都是水平地散布在所有对象层次中,但是它和对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理也是如此。这种代码就称为横切代码,在OOP设计中,它会导致了大量代码的重复,而不利于各个模块的重用。

  • AOP的核心就是代理,不用修改任何已经编写好的代码,只要使用代理就可以灵活的加入任何东西,将来不喜欢了,不用也不会影响原来的代码。

JDK动态代理

  • 代理设计模式的原理: 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理.代理对象决定是否以及何时将方法调用转到原始对象上。
    在这里插入图片描述
  • JDK动态代理开发
  • 接口类
 package com.wmy.spring.ao;

import java.math.BigDecimal;

public interface BankService {

	
	public void transfer(String target, String source, BigDecimal money);
	
	public String withdraw(String account, BigDecimal money);
	
	public int saveMoney(String account,BigDecimal money);
}
  • 接口实现类
 package com.wmy.spring.ao;

import java.math.BigDecimal;

public class BankServiceImpl implements BankService{
	
	public void transfer(String target, String source, BigDecimal money) {		
	}
	
	public String withdraw(String account, BigDecimal money) {		
		return "AOP";
	}

	public int saveMoney(String account, BigDecimal money) {
		
		return 100;
	}
}
  • 代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ServiceProxy implements InvocationHandler{

	private Object target;
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		
		System.out.println("方法:" + method.getName() + "被执行");
		
		for(Object arg : args) {
			System.out.println("参数:" + arg);
		}
		
		Object returnValue = method.invoke(target, args);		
		System.out.println("方法:" + method.getName() + "执行完毕,返回值:" + returnValue);		
		return returnValue;
	}

	
	public Object createProxy(Object target) {
		
		this.target = target;		
		return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);
	}
	
}
  • 测试
package com.wmy.spring.ao;

import java.math.BigDecimal;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		BankService bs = new BankServiceImpl();
		BankService bsProxy = (BankService) new ServiceProxy().createProxy(bs);
		bsProxy.withdraw( "李四", new BigDecimal("500"));
		bsProxy.saveMoney("张三", new BigDecimal("10000"));
	}

}

AOP的优势

  • 降低模块耦合度
  • 使系统容易扩展
  • 更好的代码复用性

AOP的术语

  • 切面(Aspect)

一个横切关注点的模块化,这个关注点可能会横切多个对象。它是横切关注点的另一种表达方式。

  • 连接点(Joinpoint)

在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。

  • 切入点(Pointcut)

匹配连接点的断言。它通常是一个表达式,有专门的语法,用于指明在哪里(或者说在哪些方法调用上)嵌入横切逻辑

  • 通知(Advice)

在切面的某个特定的连接点上执行的动作,也就是我们前面提到的横切逻辑,如日志处理逻辑,事务处理逻辑。

  • 目标对象(Target Object)

被一个或者多个切面所通知的对象,也被称作被通知对象

  • 代理对象(Proxy Object)

AOP框架创建的对象,它和目标对象遵循同样的接口,使用它的方式和使用目标对象的方式是一样的,但是它是目标对象的增强版,“通知”中的代码执行将会被代理对象的方法调用触发。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

二.Spring AOP开发

方法一:利用xml

1. 相关jar包

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.0.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

2. xml配置文件

表达式:expression=“execution(* springboot.aop.xml. * .* (…))”
第一个表示不限制返回值类型
第二个
表示springboot.aop.xml包下所有的JAVA BEAN
第三个* javabean 所有的方法
(…) 表示对方法的参数没有限制

<aop:config>
		<aop:pointcut expression="execution(* springboot.aop.xml.*.*(..))" id="loggerPointCut"/>
		<aop:aspect ref="loggerAspect">
			<aop:before method="logerBefore" pointcut-ref="loggerPointCut"/>
		</aop:aspect>

	</aop:config>

3. 业务代码

通知方法的编写

  • 前置通知:在方法执行之前执行。

xml:

<aop:before method="logerBefore" pointcut-ref="loggerPointCut"/>
 

通知类java类:

public void logerBefore(JoinPoint jp) {
		String methodName = jp.getSignature().getName();
		System.out.println("method: " + methodName + "将要被执行!");
		
		Object[] args = jp.getArgs();
		for(Object arg : args) {
			System.out.println("=============参数:>" + arg);
		}
		
	}
  • 后置通知:在方法执行之后执行

xml:

<aop:after method="logerAfter" pointcut-ref="loggerPointCut"/>

通知类java类:

public void logerAfter(JoinPoint jp) {
		String methodName = jp.getSignature().getName();
		System.out.println("method: " + methodName + "已经被执行!");
		
		Object[] args = jp.getArgs();
		for(Object arg : args) {
			System.out.println("=============参数:>" + arg);
		}
	}
  • 返回通知:在连接点完成之后执行的, 即连接点返回结果或者抛出异常的时候。

属性 returning 的值必须与方法logerAfterReturning 返回值的返回值参数名保持一致

xml:

 <aop:after-returning method="logerAfterReturning" pointcut-ref="loggerPointCut" returning="returnValue"/>

通知类java类:

public void logerAfterReturning(JoinPoint jp, Object returnValue) {
		String methodName = jp.getSignature().getName();
		System.out.println("后置返回通知 =========>method: " + methodName + "已经被执行!");
		System.out.println("后置返回通知  返回值:" + returnValue);
	}
  • 异常通知, 在方法抛出异常之后

xml:

 <aop:after-throwing method="loggerAfterThrowing" pointcut-ref="loggerPointCut"  throwing="exception"/>

通知类java类:

public void loggerAfterThrowing(JoinPoint jp, Exception exception) {
		System.out.println("后置异常通知:" + exception);
	}
  • 环绕通知

xml:

 <aop:around method="logerAround" pointcut-ref="loggerPointCut"/>

通知类java类:

public Object logerAround(ProceedingJoinPoint pjp) {
		String methodName = pjp.getSignature().getName();
		System.out.println("环绕通知 =========>method: " + methodName + "将要被执行!");
		Object[] args = pjp.getArgs();//獲取參數
		args[0] = "小明";
		try {
			Object returnValue = pjp.proceed(args);
			
			System.out.println("环绕通知 =========>method: " + methodName + "已经被执行返回值:! " + returnValue);

			return new BigDecimal("999999999999999");
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return null;
	}

方法二:利用注解

1.spring配置文件

<context:component-scan base-package="com.zzxtit.spring.aop.anno"></context:component-scan>

	<!-- 开启spring AOP 注解方式 自动代理 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

2.通知类java类编写

使用@Aspect 和@Component标记为切面的Spring Bean组件

  • 前置通知

第一个 * 表示不限制返回值类型
第二个* 表示包下所有的JAVA BEAN
第三个* javabean 所有的方法
(…) 表示对方法的参数没有限制

@Before("execution(* com.zzxtit.spring.aop.anno.*.*(..))")
	public void beforeAdvice(JoinPoint jp) {
		System.out.println("==================打印日志=====================");
		Object[] args = jp.getArgs();
		int index = 1;
		for(Object arg : args) {
			System.out.println("======第" + index +"个参数=======>" + arg);
		    index++;
		}
		System.out.println("方法:" + jp.getSignature().getDeclaringTypeName() + "." + jp.getSignature().getName());
	}
  • 后置通知
 @After("execution(* com.zzxtit.spring.aop.anno.*.*(..))")
	public void afterAdvice(JoinPoint jp) {
		System.out.println("-----------打印日志-----后置通知-----------------------------");
	}
  • 后置返回通知
 @AfterReturning(value = "execution(* com.zzxtit.spring.aop.anno.*.*(..))", returning="returnValue")
	public void afterReturningAdvice(JoinPoint jp, Object returnValue) {
		System.out.println("----------------后置返回通知-----------------------------" + returnValue);
	}
  • 环绕通知
@Around("execution(* com.zzxtit.spring.aop.anno.*.*(..))")
	public void aroundAdvice(ProceedingJoinPoint pjp) {
		
		Object returnValue = null;
		try {
			returnValue = pjp.proceed();//执行目标方法
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("----------------环绕通知-----------------------------" + returnValue);
		
	}
  • 异常通知
@AfterThrowing(value = "execution(* com.zzxtit.spring.aop.anno.*.*(..))", throwing="exception")
	public void afterThrowing(JoinPoint jp, Exception exception) {
		
	}

3.通过pointCut进行切入点的代码抽离

    @Pointcut("execution(* com.zzxtit.spring.aop.anno.*.*(..))")
	public void loggerPointCut() {
		
	}
	@After("loggerPointCut()")
	public void afterAdvice(JoinPoint jp) {
		System.out.println("-----------打印日志-----后置通知-----------------------------");
	}
	/**
	 * 如果指定 pointcut的值,将会覆盖value属性值
	 * @param jp
	 * @param returnValue
	 */
	@AfterReturning(value = "execution(* com.zzxtit.spring.aop.anno.*.*(..))", pointcut="loggerPointCut()", returning="returnValue")
	public void afterReturningAdvice(JoinPoint jp, Object returnValue) {
		System.out.println("----------------后置返回通知-----------------------------" + returnValue);
	}

4. @Order指定切面的优先级
值从0开始,越小优先级越高。

@Order(1)
@Aspect
@Component

三.Spring JDBC开发

Jdbc的配置
 <context:property-placeholder location="config/DB.properties"/>
	
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="driverClassName" value="${mysql_driver}"></property>
		<property name="url" value="${mysql_url}"></property>
		<property name="username" value="${mysql_username}"></property>
		<property name="password" value="${mysql_passwd}"></property>
	</bean>
	
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" value="#{dataSource}"></property>
	</bean>
Java的Dao 层引用JdbcTemplate
@Repository
public class UserDaoImpl implements UserDao{

	@Autowired
	private JdbcTemplate jdbcTemplate;
	}

- 增加

 public void insertUserInfo(SysUserInfo su) {
		String sql="insert into t_sys_user (user_name, passwd, salt, real_name, avatar, phone, email, gender, create_time) "
				+ "values (?, ?, ?, ?, ?, ?, ?, ?, now())";
		
		jdbcTemplate.update(sql, su.getUserName(), su.getPasswd(), su.getSalt(),
				su.getRealName(), su.getAvatar(), su.getPhone(), 
				su.getEmail(), su.getGender());
	}

- 删除

 public void deleteUserInfo(SysUserInfo su) {
		String sql = "delete from t_sys_user  where passwd=?";
		jdbcTemplate.update(sql, su.getPasswd());
	}

- 修改

 public void updateUserInfo(SysUserInfo su) {
		String sql="update t_sys_user set user_name = ?,  salt=?, real_name=?, avatar=?, phone=?, email=?, gender=?, create_time=? where passwd=?";
		jdbcTemplate.update(sql, su.getUserName(), su.getSalt(),
				su.getRealName(), su.getAvatar(), su.getPhone(), 
				su.getEmail(), su.getGender(),su.getPasswd());
	}

- 查询

 public SysUserInfo getSysUserById(int userId) {
		String sql = "select * from t_sys_user where user_id = ?";
		List<SysUserInfo> suList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<SysUserInfo>(SysUserInfo.class), userId);
		
		if(suList != null && suList.size() > 0) {
			return suList.get(0);
		}else {
			return null;
		}
	}
JDBC中具名参数的使用

1.xml配置

<bean id="namedJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
		<constructor-arg index="0" ref="dataSource"></constructor-arg>
	</bean>

2.Dao层的使用

public void insertUserInfoByNJP(SysUser su) {
		String sql = "insert into t_sys_user (user_name, passwd, salt, real_name, avatar, phone, "
				+ "email, gender, create_time) values ({userName:}, {passwd:}, {salt:}, {realName:}, {avatar:}, {phone:}, {email:}, {gender:}, now())";
		
		Map<String, Object> paramMap = new HashMap<String, Object>();
		paramMap.put("userName", su.getUserName());
		paramMap.put("passwd", su.getPasswd());
		paramMap.put("salt", su.getSalt());
		paramMap.put("realName", su.getRealName());
		paramMap.put("avatar", su.getAvatar());
		paramMap.put("phone", su.getPhone());
		paramMap.put("email", su.getEmail());
		paramMap.put("gender", su.getGender());		
		npjTemplate.update(sql, paramMap);
		
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值