一、动态代理Proxy:
利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces),不是类(Class),也不是抽象类。在运行时才知道具体的实现,spring aop就是此原理。
一:写一个接口:
package com.openlab.dao;
public interface UserDao {
public int add(int a,int b);
}
接着写一个类实现接口:
package com.openlab.dao.Impl;
import com.openlab.pojo.PeoPle;
import lombok.Data;
import org.springframework.stereotype.Component;
@Component
@Data
public class PeopleDao {
public void save(){
System.out.println("PeopleDao中的实现类1---save");
}
public Integer test(int a,int b){
System.out.println("PeopleDao实现类执行了------加法运算");
return a+b;
}
}
测试类,写了一个InvocationHandler
内部类:
也可以写一个写一个类实现InvocationHandler
接口:
我写的是第一种内部类方式:
package com.openlab;
import com.openlab.dao.Impl.UserDaoImpl;
import com.openlab.dao.UserDao;
import com.openlab.config.Springconfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Springconfig.class);
final UserDaoImpl userDao = new UserDaoImpl();
Class[] interfaces = userDao.getClass().getInterfaces();
UserDao o = (UserDao) Proxy.newProxyInstance(MyTest.class.getClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前:"+method.getName()+"......."+ Arrays.toString(args));
Object invokes = method.invoke(userDao,args);
System.out.println("方法执行之后");
return invokes;
}
});
int add = o.add(3, 5);
System.out.println(add);
}
}
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
第一个参数(ClassLoader loader 表示用哪个类的加载器
第二参数Class<?>[] interfaces 动态类需要实现的接口个数
第三参数InvocationHandler h 动态代理方法在执行时,会调用h里面的invoke方法去执行
返回类型是Object类型,需要强转为我们需要的接口类型;
上面代码中,代理u对象,调用UserDao中的add方法时,自动执行InvocationHandler中的invoke方法。
测试结果:
spring aop
- 连接点:类里面那些方法可以被增强,这些方法称为连接点
- 切入点:实际被增强的方法
- 通知:实际增强的逻辑部分 类型:前置通知 后置通知 环绕通知 异常通知 最终通知
- 切面:动作,把通知应用到切入点过程
通过注解方式
aop注解:
Advice(通知、切面): 某个连接点所采用的处理逻辑,也就是向连接点注入的代码, AOP在特定的切入点上执行的增强处理。
@Before: 标识一个前置增强方法,相当于BeforeAdvice的功能.
@After: final增强,不管是抛出异常或者正常退出都会执行.
@AfterReturning: 后置增强,似于AfterReturningAdvice, 方法正常退出时执行.
@AfterThrowing: 异常抛出增强,相当于ThrowsAdvice.
@Around: 环绕增强,相当于MethodInterceptor.
表达式标签
表达式标签
execution():用于匹配方法执行的连接点
args(): 用于匹配当前执行的方法传入的参数为指定类型的执行方法
this(): 用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target(): 用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
within(): 用于匹配指定类型内的方法执行;
@args():于匹配当前执行的方法传入的参数持有指定注解的执行;
@target():用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@within():用于匹配所以持有指定注解类型内的方法;
@annotation:用于匹配当前执行方法持有指定注解的方法;
PeoPleDaoProxy 类
package com.openlab.dao.proxy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class PeoPleDaoProxy {
@Pointcut(value="execution(* com.openlab.*.*.*(..))")//Pointcut(切入点):
// JoinPoint的集合,是程序中需要注入Advice的位置的集合,指明Advice要在什么样的条件下才能被触发,在程序中主要体现为书写切入点表达式。
public void Poin(){
}
@Before(value = "execution(* com.openlab.dao.Impl.PeopleDao.*())") //前置通知
public void before(){
System.out.println("方法执行前");
}
@After(value = "execution(* com.openlab.dao.Impl.PeopleDao.*())") //后置通知
public void after(){
System.out.println("方法执行后");
}
@AfterReturning(value = "execution(* com.openlab.dao.Impl.PeopleDao.*())")
public void afterReturning(){
System.out.println("最终执行");
}
@AfterThrowing(value="Poin()")//异常通知
public void aferthrow(){
System.out.println("出异常了");
}
@Around(value = "execution(* com.openlab.dao.Impl.PeopleDao.*())") //环绕通知
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("执行之前环绕");
joinPoint.proceed();
System.out.println("执行之后环绕");
}
}
调用类:
package com.openlab.dao.Impl;
import com.openlab.pojo.PeoPle;
import lombok.Data;
import org.springframework.stereotype.Component;
@Component
@Data
public class PeopleDao {
public void save(){
System.out.println("PeopleDao中的实现类1---save");
}
public Integer Update(PeoPle people){
System.out.println("PeopleDao实现类执行了:"+people);
return 10;
}
public Integer test(int a,int b){
System.out.println("PeopleDao实现类执行了------加法运算");
try{
int month=1/0;
}catch(Throwable e){
System.err.println("方法出错");
}finally {
System.out.println("方法的finally");
}
return a+b;
}
}
applicationContext.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:aop="http://www.springframework.org/schema/aop"
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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.openlab"/>
<aop:aspectj-autoproxy /> //重点
</beans>
测试类
package com.openlab;
import com.openlab.dao.Impl.PeopleDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ProxyTest {
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
PeopleDao peopleDao = context.getBean(PeopleDao.class);
peopleDao.save();
Integer test = peopleDao.test(5, 7);
System.out.println(test);
}
}
测试结果:
注解方式类型的顺序:
环绕前通知 前置通知 (运行方法) 异常通知 最终通知 后置通知 环绕后通知
xml配置方式:
<aop:aspectj-autoproxy />
所以我们只需要把aop:aspectj-autoproxy</aop:aspectj-autoproxy>注释掉,切面类的注释方式就失效了。所以我就只修改了xml的配置:
<!--声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,如果没有就不能使用注解方式-->
<context:component-scan base-package="com.openlab" ></context:component-scan>
<!--扫描-->
<bean id="PeoPleDaoProxy" class="com.openlab.dao.proxy.PeoPleDaoProxy"></bean>
<!--配置找到切面类-->
<aop:config>
<aop:pointcut expression="execution(* com.openlab.*.*.*(..))" id="poin"/><!--连接点-->
<aop:aspect ref="PeoPleDaoProxy"><!--配置切面类是谁-->
<aop:before method="before" pointcut-ref="poin"></aop:before><!--配置找到切面类的前置通知方法类型-->
<aop:after method="after" pointcut-ref="poin"></aop:after>
<aop:after-returning method="afterReturning" pointcut-ref="poin"/>
<aop:after-throwing method="aferthrow" pointcut-ref="poin"/>
<aop:around method="around" pointcut-ref="poin"/>
</aop:aspect>
</aop:config>
我们发现xml配置类型的顺序和注解方式顺序不一样;最后发现是xml中。
Spring-JDBC
首先:pom.xml引入依赖
<!--JDBC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>
<!--MYSQL-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
在准备db.properties
#\u8FDE\u63A5\u8BBE\u7F6E
//com.mysql.jdbc.Driver 是 mysql-connector-java 5中的,
//com.mysql.cj.jdbc.Driver 是 mysql-connector-java 6后的
jdbc.driverClassName=com.mysql.cj.jdbc.Driver//我的mysql数据库是8.26的所以是com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/t1 //jdbc:mysql: 是指JDBC连接方式;localhost: 是指你的本机地址
jdbc.name=root //用户名
jdbc.password=root //密码
创建数据库对应的类
package com.openlab.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Classes {
private int id;
private String mnane;
private int sums;
}
dao包建接口:
package com.openlab.dao;
import com.openlab.pojo.Classes;
public interface ClassesDao {
public int add(Classes role);//添加
public int updat(Classes role);//更新
public int delete(int roleid);//删除
}
Impl包里创建类实现接口:
package com.openlab.dao.Impl;
import com.openlab.dao.ClassesDao;
import com.openlab.pojo.Classes;
import org.springframework.jdbc.core.JdbcTemplate;
public class ClassDaoImpl implements ClassesDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public int add(Classes classes) {
String sql="insert into class(id,mnane,snums) value(?,?,?)";
// 定义数组来存放SQL语句中的参数
Object[] obj = new Object[] {classes.getId(),classes.getMnane(),classes.getSums()};
// 执行添加操作,返回受SQL语句影响的条数
int num = this.jdbcTemplate.update(sql, obj);
return num;
}
@Override
public int updat(Classes classes) {
String sql="update into class(id,mnane,snums) value(?,?,?)";
// 定义数组来存放SQL语句中的参数
Object[] obj = new Object[] {classes.getId(),classes.getMnane(),classes.getSums()};
// 执行添加操作,返回受SQL语句影响的条数
int num = this.jdbcTemplate.update(sql, obj);
return num;
}
@Override
public int delete(int id) {
// 定义SQL
String sql = "delete from class where id=?";
// 执行更新操作,返回受SQL语句影响的条数
int num=this.jdbcTemplate.update(sql,id);
return num;
}
}
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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!-- 1.配置数据源 -->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<!-- 数据库驱动 -->
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<!--连接数据库的url -->
<property name="url" value="${jdbc.url}"></property>
<!--连接数据库的用户名 -->
<property name="username" value="${jdbc.name}"></property>
<!--连接数据库的密码 -->
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 默认必须使用数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="classImpl" class="com.openlab.dao.Impl.ClassDaoImpl">
<!-- 将jdbcTemplate注入到accountDao实例中 -->
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
</beans>
测试类:
package com.openlab;
import com.openlab.dao.Impl.ClassDaoImpl;
import com.openlab.pojo.Classes;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Testclass {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans2.xml");
ClassDaoImpl classimpl = context.getBean("classImpl", ClassDaoImpl.class);
Classes lala = new Classes(10, "拉拉", 38);
int adds = classimpl.add(lala);
if(adds>0) {
System.out.println("成功插入"+adds+"条数据!");
}else {
System.out.println("插入操作执行失败");
}
}
}
插入结果: