SSM的学习(五)—spring的核心Aop与JdbcTemplate
Aop
Aop(Aspect Oriented Programming),面向切面编程,是面向对象思想上的补充。
简单来说:就是在原来代码的基础之上,想要不改变源码,添加新的功能,运用场景非常之多如:打印日志 ,事务,等。
Aop的底层原理
aop基于动态代理:会给要改动的对象创建一个代理,通过代理对象来实现代码的增强
java中动态代理有两种实现方式:
jdk动态代理实现
/**
* 基于jdk动态代理的AOP底层实现
* 该类功能:对UserDAO 目标类的save方法,做一个增强操作
* 在保存之前,需要先执行权限验证,具体做法,需要对目标类产生一个代理类对象。
*/
public class JdkProxy implements InvocationHandler {
private UserDao userDao;
public JdkProxy(UserDao userDao){
this.userDao = userDao;
}
/**
* 拿到一个代理对象,改代理对象属于UserDao类型
* @return
*/
public UserDao getUserDaoProxy(){
/**
* 参数1:目标独享userDao的类加载器对象
* 参数2:目标独享的接口对象
* 参数3:实际增强的代码,需参数3完成
*/
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this);
return userDaoProxy;//返回代理对象
}
@Override
public 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);//如果不是save直接放行
}
}
cglib动态代理实现
import com.spring.anno.UserDao;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
private UserDao userDao;
public CglibProxy(UserDao userDao){
this.userDao = userDao;
}
//产生代理对象
public UserDao getUserDaoProxy(){
//1:创建Enhancer对象
Enhancer enhancer = new Enhancer();
//2:拿到enhancer的父类,因为Cglib是基于继承关系实现的动态代理
enhancer.setSuperclass(userDao.getClass());
//3:执行回调,等同于jdk动态代理的InvocationHandler操作
enhancer.setCallback(this);
//4:创建代理对象
return (UserDao)enhancer.create();
}
@Override
/**
* 增强代码的操作,权限验证的逻辑代码
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if ("save".equals(method.getName())){
System.out.println("拿到权限,执行修改操作。。。。");
return methodProxy.invokeSuper(proxy,args);
}
return methodProxy.invokeSuper(proxy,args);
}
}
实现过程:
创建bean
@Component
public class UserDao {
public void save(){
System.out.println("保存用户。。。");
}
public void update(){
System.out.println("更新用户用户。。。");
}
public void delete(){
System.out.println("删除用户。。。");
}
public void find(){
//用于测试异常通知
int a =1;
int b =0;
int c = a/b;
System.out.println("查询用户。。。"+c);
}
}
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"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--扫描包-->
<context:component-scan base-package="com.spring"></context:component-scan>
<!--配置开启Aop切面类的注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
创建切面
@Component
@Aspect
public class MyAspectAnno {
@Pointcut(value = "execution(* com.spring.anno.UserDao.save(..))")
private void pointcut(){
}
/**
* 前置通知
*/
@Before(value = "pointcut()")
public void before(){
System.out.println("前置通知。。。。。");
}
/**
* 后置通知
*/
@After(value = "execution(* com.spring.anno.UserDao.update(..))")
public void after(){
System.out.println("后置通知。。。。。");
}
/**
* 返回通知:即目标有返回值时会触发,改注解中的returning属性表示目标方法返回值的变量
* 这个需要和目标方法的参数类型对应,否则拦截不到,如果想拦截所有,方法的返回值参数可以设置为Object
* @param result
*/
@AfterReturning(value = "execution(* com.spring.anno.UserDao.delete(..))" ,returning = "result")
public void returning(Object result){
System.out.println("返回通知。。。。。"+result);
}
/**
*
* @param joinPoint
* @param e 目标方法所抛出的异常,这个参数是目标方法抛出的异常或异常的父类,否则捕获不到,
* 如果想拦截所有异常参数类型可以设置为Exception
*/
@AfterThrowing(value = "execution(* com.spring.anno.UserDao.find(..))",throwing = "e")
public void throwing(JoinPoint joinPoint,Exception e){
System.out.println("异常通知。。。。。"+"方法异常"+e.getMessage());
}
@Around(value = "execution(* com.spring.anno.UserDao.save(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("环绕通知1。。。。。");
Object proceed =null;
try {
//相当于method.invoke的方法,在该方法前后可以设置类似于前后通知的方法
proceed = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕通知2。。。。。");
}
}
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpringTest {
@Resource(name = "userDao")
private UserDao userDao;
@Test
public void test(){
userDao.save();
userDao.delete();
userDao.update();
userDao.find();
}
}
打印结果:
AOP的详细分析
那么有人会问了,到底什么时候使用jdk动态代理?什么时候使用cglib动态代理呢?
我们在spring源码中找出了答案:当你动态代理的对象是接口时使用jdk,当是class时则使用cglib。
于是又有喜欢问问题的要问了:为什么接口只能使用jdk动态代理?
源码中也有答案:因为我们申请的代理对象已经继承了Proxy,(我们都知道java是单继承的吧!)所以无法在继承了,只能继承接口了。
JdbcTemplate
Spring的jdbc模板
案例
1:首先创建一个如图的表:
2:创建该表的实体类
public class User {
private int id;
private String name;
private double money;
//get、set方法已省略
public User() {
}
public User(int id, String name, double money) {
this.id = id;
this.name = name;
this.money = money;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
3:创建javaConfig
import com.mchange.v2.c3p0.DriverManagerDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
public class JdbcConfig {
/**
*交给spring容器管理
*/
@Bean
DataSource dataSource(){
//创建数据库连接池(该连接池是spring框架自己的连接池)
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//配置连接mysql的操作
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///数据库名");
dataSource.setUser("账号");
dataSource.setPassword("密码");
return dataSource;
}
/**
*创建jdbc模板交给spring管理
*/
@Bean
JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
}
4:测试
import com.spring.bean.User;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class Main {
private JdbcTemplate jdbcTemplate;
@BeforeEach //所有测试方法之前调用
public void before(){
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JdbcConfig.class);
jdbcTemplate = ctx.getBean(JdbcTemplate.class);
}
@Test
public void inseert(){
jdbcTemplate.update("insert into tb_account(name,money)values(?,?);","Jon",1000d);
}
@Test
public void update(){
jdbcTemplate.update("update tb_account set name =?,money =? where id =?","TOM",1000d,5);
}
@Test
public void delete(){
jdbcTemplate.update("delete from tb_account where id =?",5);
}
/**
* 需要要注意BeanPropertyRowMapper要求查出的字段和bean一致
*/
@Test
public void select(){
User user= jdbcTemplate.queryForObject("select * from tb_account where id = ?",new BeanPropertyRowMapper<User>(User.class),1);
System.out.println(user);
}
/**
* 自定义RowMapper
*/
@Test
public void select1(){
User user= jdbcTemplate.queryForObject("select * from tb_account where id = ?",new RowMapper<User>(){
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
int id = rs.getInt("id");
String name = rs.getString("name");
Double money = rs.getDouble("money");
User user = new User();
user.setId(id);
user.setName(name);
user.setMoney(money);
return user;
}
},1);
System.out.println(user);
}
/**
* 查询多条记录
*/
@Test
public void select2(){
List<User> user = jdbcTemplate.query("select * from tb_account ",new BeanPropertyRowMapper<>(User.class));
System.out.println(user);
}
}