代理模式
它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。
AOP
1、什么是AOP:面向切面编程。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。不通过修改源代码方式,添加新的功能
底层原理
1、AOP底层使用动态代理
(1)有两种情况的动态代理
第一种 有接口情况,使用JDK动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口,在com.sun.proxy包下,类名为$proxy2。
- 创建接口实现类代理对象,增强类方法。
第二种 没有接口情况,使用CHLIB动态代理,最终生成的代理类会继承目标类,并且和目标类在相同包下
- 创建子类的代理对象,增强类方法
JDK动态代理
1、使用JDK动态代理,使用Proxy类里面的方法创建代理对象
(1)调用newProxyInstance
方法有三个参数:
- ClassLoader loader:指定加载动态生成的代理类的类加载器
- 增强方法所在类,这个类实现的接口的数组,支持多个接口
- 实现InvocationHandler,创建代理对象,写增强方法
2、编写JDK动态代理代码
(1)创建接口,定义方法
public interface UserDao {
public int add(int a,int b);
public String update(String a);
}
(2)创建接口实现类
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
System.out.println("add is running...");
return a+b;
}
@Override
public String update(String a) {
return a;
}
}
(3)使用Proxy类创建接口代理对象
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
//可以通过匿名内部类实现InvocationHandler接口
// Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// return null;
// }
// });
UserDaoImpl userDao = new UserDaoImpl();
UserDaoProxy userDaoProxy = new UserDaoProxy(userDao);
UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, userDaoProxy);
System.out.println(dao.add(1,2));
}
}
class UserDaoProxy implements InvocationHandler {
Object object = new Object();
UserDaoProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy表示代理对象,method表示要执行的方法,args表示要执行的方法的参数列表
//方法之前
System.out.println("方法之前执行..." + method.getName() + "参数为:" + Arrays.toString(args));
//被增强的方法执行,参数为被增强方法所在类
Object res = method.invoke(object,args);
//方法之后
System.out.println("方法之后执行...");
return res;
}
}
package com.atguigu.spring.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxy() {
ClassLoader classLoader = this.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
System.out.println("[动态代理][日志] " + method.getName() + ",参数:" + Arrays.toString(args));
result = method.invoke(target, args);
System.out.println("[动态代理][日志] " + method.getName() + ",结果:" + result);
} catch (Exception e) {
e.printStackTrace();
System.out.println("[动态代理][日志] " + method.getName() + ",异常:" + e.getMessage());
} finally {
System.out.println("[动态代理][日志] " + method.getName() + ",方法执行完毕");
}
return result;
}
};
return Proxy.newProxyInstance(classLoader, interfaces, h);
}
}
AOP(术语)面向切面编程
①横切关注点
从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方
法进行多个不同方面的增强。
②通知
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
- 前置通知Before:在被代理的目标方法前执行
- 返回通知AfterReturning:在被代理的目标方法成功结束后执行
- 异常通知AfterThrowing:在被代理的目标方法异常结束后执行(catch)
- 后置通知After:在被代理的目标方法最终结束后执行(finally)
- 环绕通知Around:使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所
有位置
③切面
封装通知方法的类。
④目标
被代理的目标对象。
⑤代理
向目标对象应用通知之后创建的代理对象。
⑥连接点
抽取横切关注点的位置
⑦切入点
定位连接点的方式。
每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物(从逻辑上来说)。
如果把连接点看作数据库中的记录,那么切入点就是查询记录的 SQL 语句。
Spring 的 AOP 技术可以通过切入点定位到特定的连接点。
切点通过 org.springframework.aop.Pointcut
作用
简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,
提高内聚性。
代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就
被切面给增强了。
AOP(准备)
AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面
向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况
下给程序动态统一添加额外功能的一种技术。
1、Spring框架一般都搜基于AspectJ实现AOP操作
(1)什么是AspectJ
AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
2、基于AspectJ实现AOP操作
(1)基于xml配置文件实现
(2)基于注解方式实现(常用)
3、在项目工程中引入AOP相关依赖
4、切入点表达式
(1)切入点表达式作用:知道对哪个类里面哪个方法进行增强
(2)语法结构:
execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]))
举例1:对com.ayu.spring.dao.bookDao类里面的add进行增强
excecution(*com.ayu.spring.dao.bookDao.add(..))
** 表示public private所有权限均可*
举例2:对com.ayu.spring.dao.bookDao类里面的所有方法进行增强
excecution(*com.ayu.spring.dao.bookDao.*(..))
举例3:对com.ayu.spring.dao里面的所有类和类里面的所有方法进行增强
excecution(*com.ayu.spring.dao.*.*(..))
AOP操作(AspectJ注解)
切面类和目标类都需要交给IOC容器管理
1、创建类,在类里面定义方法
@Component
public class User {
public void add(){
System.out.println("add...");
}
}
2、创建增强类,编写增强逻辑
(1)在增强类里面创建方法,不同的方法代表不同的通知类型
@Component
@Aspect /标注切面
public class UserProxy {
@Before("execution(* com.ayu.spring.aopano.User.add(..))")
public void before(){
System.out.println("before...");
}
}
3、进行通知的配置
(1)在spring的配置文件中,开启注解扫描
<?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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="com.ayu.spring.aopano"/>
<!--开启生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
(2)使用注解创建User和UserProxy对象
(3)在增强类上面添加注解@Aspect
(4)在spring配置文件中开启生成代理对象
4、配置不同类型的通知
(1)在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
@Component
@Aspect
public class UserProxy {
@Before("execution(* com.ayu.spring.aopano.User.add(..))")
public void before(){
System.out.println("before...");
}
@After("execution(* com.ayu.spring.aopano.User.add(..))")
public void After(){
System.out.println("After...");
}
@AfterThrowing("execution(* com.ayu.spring.aopano.User.add(..))")
public void AfterThrowing(){
System.out.println("AfterThrowing...");
}
@AfterReturning("execution(* com.ayu.spring.aopano.User.add(..))")
public void AfterReturning(){
System.out.println("AfterReturning...");
}
@Around("execution(* com.ayu.spring.aopano.User.add(..))")
public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕前...");
proceedingJoinPoint.proceed();
System.out.println("环绕后...");
}
}
5、相同的切入点提取
@Pointcut(value = "execution(* com.ayu.spring.aopano.User.add(..))")
public void pointdemo(){
}
@Before(value = "pointdemo()")
public void before() {
System.out.println("before...");
}
6、有多个增强类对同一个方法增强,设置增强类优先级
(1)在增强类上面添加注解@Order(数字类型值),默认值为Interger的最大值,数字类型值越小优先级越高。
7、完全注解开发
config.ConfigAop
@Configuration
@ComponentScan(basePackages = {"com.ayu.spring"})
@EnableAspectJAutoProxy
public class ConfigAop {
}
9、为接口实现类设置通知
@Component
@Aspect
public class LoggerAspect {
@Before("execution(public int com.atguigu.spring.aop.annotation.CalculatorImpl.add(int ,int ))")
public void beforeAdviceMethod(){
System.out.println("LoggerAspect,前置通知");
}
}
@Test
public void testHello(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop-annotation.xml");
Calculator calculator = context.getBean(Calculator.class);
int add = calculator.add(1, 1);
}
9、获取连接点所对应方法的信息
在通知方法的参数位置,设置JoinPoint类型的参数,就可以获取连接点所对应方法的信息。
- 签名信息:方法的声明(访问修饰符、返回值类型、方法名、参数)
@Before("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
public void beforeAdviceMethod(JoinPoint joinPoint){
//获取连接点所对应方法的签名信息
Signature signature = joinPoint.getSignature();
//获取方法名
String name = signature.getName();
//获取连接点所对应方法的参数
Object[] args = joinPoint.getArgs();
System.out.println("LoggerAspect,前置通知");
}
- 在返回通知中可以获取目标对象方法的返回值,只需要通过@AfterReturning注解的returning,就可以将通知方法的某个参数指定为接收目标对象方法的返回值的参数
@AfterReturning(value = "pointCut()",returning = "result")
public void afterReturningAdviceMethod(JoinPoint joinPoint,Object result){
//获取连接点所对应方法的签名信息
Signature signature = joinPoint.getSignature();
System.out.println("LoggerAspect,方法:"+signature.getName()+",结果:"+result);
}
- 在异常通知中可以获取目标对象方法的异常,只需要通过@AfterThrowing注解的throwing,就可以将通知方法的某个参数指定为接收目标对象方法的出现的异常的参数
@AfterThrowing(value = "pointCut()",throwing = "ex")
public void afterThrowingAdviceMethod(JoinPoint joinPoint,Throwable ex){}
10、环绕通知
@Around("pointCut()")
public Object Around(ProceedingJoinPoint proceedingJoinPoint){
Object result =null;
try {
System.out.println("环绕通知-->前置通知");
proceedingJoinPoint.proceed();
System.out.println("环绕通知-->返回通知");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕通知-->异常");
}finally {
System.out.println("环绕通知-->后置通知");
}
return result;
}
AOP操作(AspectJ配置文件–了解)
1、创建两个类,增强类和被增强类,创建方法
public class Book {
public void buy(){
System.out.println("buy...");
}
}
public class BookProxy {
public void before(){
System.out.println("before...");
}
}
2、在spring配置文件中创建两个类对象
3、在spring配置文件中配置切入点
<?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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="book" class="com.ayu.spring.aopxml.Book"></bean>
<bean id="bookProxy" class="com.ayu.spring.aopxml.BookProxy"></bean>
<aop:config>
<aop:pointcut id="p" expression="execution(* com.ayu.spring.aopxml.Book.buy(..))"/>
<!--将IOC容器中的某个bean设置为切面-->
<aop:aspect ref="bookProxy" >
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>
</beans>
JdbcTemplate
概念和准备
1、什么是JdbcTemplate:Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库操作
2、准备工作
(1)引入相关jar包
(2)在spring配置文件中配置连接池
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql:///user_db"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
(3)配置Jdbc对象,注入DataSource
<!--JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
(4)创建service类,创建dao类,在dao注入jdbcTemplate
- 配置文件
<?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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.ayu.spring"/>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://127.0.0.1:3306/user_db"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!--JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
- service
@Service
public class BookService {
@Autowired
private BookDao bookDao;
}
- dao
@Repository
public class BookDaoImpl implements BookDao{
@Autowired
private JdbcTemplate jdbcTemplate;
}
JdbcTemplate操作数据库
添加
1、对应数据库表,创建实体类
package com.ayu.spring.entity;
public class Book {
private int book_id;
private String book_name;
private String book_status;
public int getBook_id() {
return book_id;
}
public void setBook_id(int book_id) {
this.book_id = book_id;
}
public String getBook_name() {
return book_name;
}
public void setBook_name(String book_name) {
this.book_name = book_name;
}
public String getBook_status() {
return book_status;
}
public void setBook_status(String book_status) {
this.book_status = book_status;
}
}
2、编写service和dao
(1)在dao进行数据库添加操作
public interface BookDao {
public void add(Book book);
}
@Service
public class BookService {
@Autowired
private BookDao bookDao;
public void addBook(Book book){
bookDao.add(book);
}
}
(2)调用JdbcTemplate对象里面的update方法实现添加操作
- 第一个参数:sql语句
- 第二个参数:可变参数,设置sql语句值
@Repository
public class BookDaoImpl implements BookDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void add(Book book) {
String sql = "insert into t_book values(?,?,?)";
Object[] args = {book.getBook_id(),book.getBook_name(),book.getBook_status()};
jdbcTemplate.update(sql,args);
}
}
3、测试类
@Test
public void testAdd(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
BookService bookService = context.getBean("bookService",BookService.class);
Book book = new Book();
book.setBook_id(2);
book.setBook_name("anna");
book.setBook_status("normal");
bookService.addBook(book);
}
spring整合junit4
//指定当前测试类在spring的测试环境中执行,此时就可以通过注入的方式直接获取IOC容器中的bean
@RunWith(SpringJUnit4ClassRunner.class)
//测试Spring测试环境的配置文件
@ContextConfiguration("classpath:spring-jdbc.xml")
public class JdbcTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testAdd(){
String sql = "insert into t_user values (null,?,?,?,?,?)";
jdbcTemplate.update(sql,"gh","123","24","男","158@qq.com");
}
}
修改和删除
@Override
public void upadte(Book book) {
String sql ="update t_book set b_name=?,b_status=? where b_id=?";
Object[] args = {book.getBook_name(),book.getBook_status(),book.getBook_id()};
int update = jdbcTemplate.update(sql,args);
System.out.println(update);
}
@Override
public void delete(int id) {
String sql = "delete from t_book where b_id=?";
int update = jdbcTemplate.update(sql,id);
System.out.println(update);
}
查询返回某个值
1、查询表里有多少条记录,返回某个值
有两个参数
- 第一个参数:sql语句
- 第二个参数:返回类型Class
@Override
public int findCount() {
String sql = "select count(*) from t_book";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
查询返回对象
1、场景:查询图书详情信息
2、JdbcTemplate实现查询返回对象
有三个参数
- 第一个参数:sql语句
- 第二个参数:RowMapper是接口,针对返回不同类型数据,使用这个接口里面的实现类完成数据的封装
- 第三个参数:sql语句值
需要Book实体类的属性名与数据库的字段名保持一致,否则获取不到(取别名?)
@Override
public Book findBook(String id) {
String sql = "select * from t_book where book_id=?";
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
System.out.println(book);
return book;
}
查询返回集合
1、场景:查询图书列表分页
2、JdbcTemplate实现
有三个参数
- 第一个参数:sql语句
- 第二个参数:RowMapper是接口,针对返回不同类型数据,使用这个接口里面的实现类完成数据的封装
- 第三个参数:sql语句值
@Override
public List<Book> findAllBook() {
String sql = "select * from t_book";
List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
return bookList;
}
批量操作
操作表里面多条记录
1、JdbcTemplate批量添加
有两个参数
- 第一个参数:sql语句
- 第二个参数:List集合,添加多条记录数据
@Override
public void batchAdd(List<Object[]> args) {
String sql = "insert into t_book values(?,?,?)";
int[] ints = jdbcTemplate.batchUpdate(sql, args);
System.out.println(Arrays.toString(ints));
}
List<Object[]> args = new ArrayList<>();
Object[] o1 = {"3","java","a"};
Object[] o2 = {"4","java","b"};
Object[] o3 = {"5","java","c"};
args.add(o1);
args.add(o2);
args.add(o3);
bookService.batchAdd(args);
2、 JdbcTemplate批量修改
@Override
public void batchUpdate(List<Object[]> args) {
String sql ="update t_book set book_name=?,book_status=? where book_id=?"; //删除改为String sql ="delete from t_book where book_id=?";即可
int[] ints = jdbcTemplate.batchUpdate(sql, args);
System.out.println(Arrays.toString(ints));
}