Spring 简介
Spring:春天—-> 给软件行业带来了春天
2002,首次推出了Spring框架的出行:interface21框架
Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日正式发布1.0版本
Spring理念:是现有的技术更加容易使用,本身是一个大杂烩。
SSH:Struct2 + Spring + Hibernate
SSM: SpringMVC + Spring + Mybatis
官网: https://spring.io/projects/spring-framework#overview
官方下载: https://repo.spring.io/release/org/springframework/spring/
GitHub: https://github.com/spring-projects/spring-framework
优点
spring是开源的免费的容器。
spring是一个轻量级的,非入侵式的框架。
控制反转(IOC),面向切面编程 (AOP)。
支持事务处理,对框架整合的支持。 ==总结:spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。==
组成
拓展 现代化的Java开发!说白就是基于Spring的开发!
Spring Boot
一个快速开发的脚手架。
基于SpringBoot可以快速的开发单个微服务。
约定大于配置。
Spring Cloud
SpringCloud是基于SpringBoot实现的。
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!承上启下的作用!
弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”
IOC组成理论推导 ==(实质上就是把控制器给用户让他给参数从而调用通用方法去实现业务,增加set方法变成调用参数传入) ==
原来的实现方式:
1.UserDao接口
1 2 3 public interface UserDao { void getUser(); }
2.UserDaoImpl实现类
1 2 3 4 5 public class UserDaoImpl implements UserDao{ public void getUser() { System.out.println("默认获取用户的数据"); } }
3.UserService业务接口
1 2 3 public interface UserService { void getUser(); }
4.UserServiceImpl实现类
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoImpl();
public void getUser() {
userDao.getUser();
}
}
如果想要改变,就需要每次更改UserDao若将UesrDao使用Set接口实现
public class UserServiceImpl implements UserService{
private UserDao userDao;
//利用set进行动态实现值得注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
之前,程序通过new主动创建对象!控制权在程序猿手上
使用set注入后,程序不再具有主动性,而是变成了被动的接受对象!
这种思想,从本质上解决了问题,程序员不用再去管理对象的创建了,降低了耦合性!
1 2 3 4 5 6 7 8 总结:在以前的开发模式中,假设我现在有增加一个新的DAO了,然后要去不断的new 不断的改变Service层,显然这种情况并不是好的代码,所以我们可以用反射来改变这一模式,但是我们现在有了Spring,就先来看看Spring吧 以前是不断的new new new 现在加入这个代码这种,专门来选择我们要的不同DAO层(其实就是一种模式:代理模式) 1 )使用set注入后,程序不再具有主动性,而是变成了被动的接受对象!(这个就是) public void setUserDAO(UserDAO userDAO) { this .userDao = userDao; } 2 )这种思想,从本质上解决了问题,程序员不用再去管理对象的创建了,降低了耦合性!
IOC本质 控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IOC的一种方法 , 也有人认为DI只是IOC的另一种说法(不正确的) 。没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓的控制反转就是:获得依赖的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(xml或注解)并通过第三方去生产或获取特定对象的方式。在spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)
HelloSpring 先导入Spring依赖
后编写hello实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.yang.pojo;public class hello { private String name; public String getName () { return name; } public void setName (String name) { this .name = name; } @Override public String toString () { return "Hello{" + "name='" + name + '\'' + '}' ; } }
==service实体类中可以实现其他实体类的方法从而调用不同的接口实现业务处理这样就只用在xml中改用对应参数就能实现对应输出语句 ==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.yang.service;import com.yang.dao.daouser;public class userimpl implements service { daouser daouser; public void setDaouser (com.yang.dao.daouser daouser) { this .daouser = daouser; } @Override public void get () { daouser.get(); } }
编写bean.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="hello" class ="com.yang.pojo.hello" > <property name ="name" value ="sl" /> </bean > <bean id ="mysql" class ="com.yang.dao.mysql" > </bean > <bean id ="oracle" class ="com.yang.dao.oracle" > </bean > <bean id ="userimpl" class ="com.yang.service.userimpl" > <property name ="daouser" ref ="oracle" > </property > </bean > </beans >
最后测试(代码一开始的基本不变,变的是xml中的参数,就是调用什么接口)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import com.yang.pojo.hello;import com.yang.service.userimpl;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class text { public static void main (String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext ("bean.xml" ); hello hello = (hello) context.getBean("hello" ); System.out.println(hello); userimpl userimpl = (userimpl) context.getBean("userimpl" ); userimpl.get(); } }
IOC创建对象的方式
使用无参构造创建对象,默认!
假设我们要使用有参构造创建对象。
1.下标赋值
1 2 3 4 <bean id ="user" class ="com.kuang.pojo.User" > <constructor-arg index ="0" value ="狂神说Java" /> </bean >
2.类型(不建议使用)
1 2 3 4 <bean id ="user" class ="com.kuang.pojo.User" > <constructor-arg type ="java.lang.String" value ="lifa" /> </bean >
==3.参数名(推荐用这个) ==
1 2 3 4 <bean id ="user" class ="com.kuang.pojo.User" > <constructor-arg name ="name" value ="李发" /> </bean >
==总结:在配置文件加载的时候,容器中管理的对象就已经初始化了! ==
Spring配置 1.别名(一般不用用下面的name就能很好的取代别名的作用)
1 2 <alias name ="user" alias ="userNew" />
2.Bean的配置
1 2 3 4 5 6 7 8 <bean id ="userT" class ="com.kuang.pojo.UserT" name ="user2 u2,u3;u4" > <property name ="name" value ="黑心白莲" /> </bean >
3.import 这个import。一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。 假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
张三
李四
王五
applicationContext.xml
1 2 3 <import resource="bean.xml"/> <import resource="bean2.xml"/> <import resource="bean3.xml"/>
==使用的时候,直接使用总的配置(applicationContext.xml)就可以了。而且spring会智能的将多个xml重复的合并并且调用寻找对应的bean ==
依赖注入 构造器注入 前面已经介绍过,参考IOC创建对象的方式
Set方式注入【重点】
依赖注入:Set注入
依赖:bean对象的创建依赖于容器!
注入:bean对象中的所有属性,由容器来注入!
【环境搭建】
复杂类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Address { private String address; public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } @Override public String toString () { return "Address{" + "address='" + address + '\'' + '}' ; } }
真实测试对象
1 2 3 4 5 6 7 8 9 10 11 public class Student { private String name; private Address address; private String[] books; private List<String> hobbies; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; }
beans.xml
1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="student" class ="com.kuang.pojo.Student" > <property name ="name" value ="l" /> </bean > </beans >
测试类
==context.getBean(“student”,student.class)第二个写上student的反射对象就可以不用强转 ==
1 2 3 4 5 6 7 8 public class MyTest { public static void main (String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext ("beans.xml" ); Student student = context.getBean("student" ,student.class); System.out.println(student.getName()); } }
完善注入信息
==特别注意类注入ref,map是用entry,null可以自闭合和properties标签的不同 ==
==其他注入格式类似==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 <bean id ="address" class ="com.kuang.pojo.Address" > <property name ="address" value ="西安" /> </bean > <bean id ="student" class ="com.kuang.pojo.Student" > <property name ="name" value ="lf" /> <property name ="address" ref ="address" /> <property name ="books" > <array > <value > 红楼梦</value > <value > 西游记</value > <value > 水浒传</value > <value > 三国演义</value > </array > </property > <property name ="hobbies" > <list > <value > 打篮球</value > <value > 看电影</value > <value > 敲代码</value > </list > </property > <property name ="card" > <map > <entry key ="身份证" value ="123456789987456321" /> <entry key ="银行卡" value ="359419496419481649" /> </map > </property > <property name ="games" > <set > <value > LOL</value > <value > csgo</value > </set > </property > <property name ="wife" > <null /> </property > <property name ="info" > <props > <prop key ="driver" > 20210122</prop > <prop key ="url" > 102.0913.524.4585</prop > <prop key ="user" > lf</prop > <prop key ="password" > 123456</prop > </props > </property > </bean >
pc命名空间方式注入 我们可以使用p命名空间和c命名空间进行注入
==p命名就是set方式注入,需要无参构造和set方法,c命名就是类似于构造器注入,需要有参构造 ==
==注意点:p命名和c命名空间不能直接使用,需要导入xml约束! 格式p/c加冒号 ==
1 2 xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
user实体类要加有参(c报错)无参(p报错)构造不然会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class User { private String name; private int age; public User () {} public User (String name,int age) { this .name = name; this .age = age; } public void setName (String name) { this .name = name; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "User{" + "name='" + name + '\'' + ", age=" + age + '}' ; } }
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?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:p ="http://www.springframework.org/schema/p" xmlns:c ="http://www.springframework.org/schema/c" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="user" class ="com.kuang.pojo.User" p:name ="lf" p:age ="19" /> <bean id ="user2" class ="com.kuang.pojo.User" c:name ="ly" c:age ="11" /> </beans >
测试:
1 2 3 4 5 6 7 8 9 10 @Test public void test2 () { ApplicationContext context = new ClassPathXmlApplicationContext ("userbeans.xml" ); User user = context.getBean("user" ,User.class); System.out.println(user); User user2 = context.getBean("user2" ,User.class); System.out.println(user2); }
bean的作用域
单例模式(Spring默认机制)重点了解一般单线程用这个
1 <bean id ="user2" class ="com.kuang.pojo.User" c:name ="狂神" c:age ="22" scope ="singleton" />
原型模式:每次从容器中get的时候,都会产生一个新对象!实际上就是从一个对象克隆多个对象,他们的地址值不同(hashcode)
1 <bean id ="user2" class ="com.kuang.pojo.User" c:name ="狂神" c:age ="22" scope ="prototype" />
其余的request、session、application、这些只能在web开发中使用到!
Bean的自动装配
自动装配是Spring满足bean依赖一种方式!
Spring会在上下文中自动寻找,并自动给bean装配属性!
在Spring中有三种装配的方式:
在xml中显式的配置;(这之前的都是显示配置)
在java中显式配置;(以后再出现)
隐式的自动装配bean【重要】
测试 环境搭建:创建项目,一个人有两个宠物!
——-一猫一狗一人三对象实体类,人中有三属性猫狗名字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="dog" class ="com.kuang.pojo.Dog" /> <bean id ="cat" class ="com.kuang.pojo.Cat" /> <bean id ="user" class ="com.kuang.pojo.User" > <property name ="cat" ref ="cat" /> <property name ="dog" ref ="dog" /> <property name ="str" value ="lf" /> </bean > </beans >
ByName自动装配 autowire byName (按名称自动装配)
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。
采用自动装配将避免这些错误,并且使配置简单化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="dog" class ="com.kuang.pojo.Dog" /> <bean id ="cat" class ="com.kuang.pojo.Cat" /> <bean id ="people" class ="com.kuang.pojo.People" autowire ="byName" > <property name ="name" value ="lf" /> </bean > </beans >
小结:
当一个bean节点带有 autowire byName的属性时。
将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
去spring容器中寻找是否有此字符串名称id的对象。
如果有,就取出注入;如果没有,就报空指针异常java.lang.NullPointerException。因为按byName规则找不对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。。
ByType自动装配 autowire byType (按类型自动装配)
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。
1 2 3 4 5 6 <bean id ="people" class ="com.kuang.pojo.People" autowire ="byType" > <property name ="name" value ="lf" /> </bean >
小结:
ByName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
ByType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致且保证全局唯一 !
使用注解实现自动装配
导入约束
配置注解的支持
1 2 3 4 5 6 7 8 9 10 11 12 13 <?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <context:annotation-config /> </beans >
@Autowired
直接在属性上使用即可!也可以在set方法上使用!
使用Autowired我们就可以不用编写set方法了,前提是你这个自动配置的属性在IOC(Spring)容器中存在,且符合名字ByName!
@Nullable 字段标记了了这个注解,说明这个字段可以为null;
1 2 3 public @interface Autowired { boolean required() default true; }
测试代码
1 2 3 4 5 6 7 8 public class People { @Autowired(required = false) private Cat cat; @Autowired private Dog dog; private String name; }
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value = “xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!
1 2 3 4 5 6 7 8 9 public class People { @Autowired @Qualifier(value = "cat111") private Cat cat; @Autowired @Qualifier(value = "dog222") private Dog dog; private String name; }
@Resource
1 2 3 4 5 6 7 8 9 public class People { @Resource private Cat cat; @Resource private Dog dog; }
小结:
@Resource和@Autowired的区别:
都是用来自动装配的,都可以放在属性字段上
@Autowired通过byType的方式实现,而且必须要求这个对象存在!【常用】
@Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】
执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byName的方式实现。
使用注解开发 在Spring4之后,要使用注解开发,必须保证aop的包导入了
使用注解需要导入context约束,增加注解的支持还要增加包扫描注解 !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <context:annotation-config /> <context:component-scan base-package ="com.yang" /> </beans >
1. bean注入使用@Componet注解
1 2 3 4 5 @Component public class User { String name; }
2. 属性注入使用@Value注解
1 2 3 4 5 6 7 8 9 10 11 12 @Component public class User { String name; @Value("yang") public void setName (String name) { this .name = name; } }
衍生注解 @Componet有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
dao层 【@Repository】
service层 【@Service】
controller层 【@Controller】
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean(类似于xml注册bean)
4.自动装配
1 2 3 4 @Autowired 自动装配通过类型、名字 如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx") @Nullable 字段标记了这个注解,说明这个字段可以为null @Resource 自动装配通过名字,类型
5. 作用域 @Scope(“singleton”)单例 (prototype)多例
6. 小结 XML 与 注解
xml更加万能,适用于任何场合!维护简单方便
注解不是自己类使用不了, 维护相对复杂
XML 与 注解最佳实践
xml用来管理bean
注解只负责完成属性的注入
我们在使用过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持
使用Java的方式配置Spring 我们现在可以完全不使用Spring的xml配置了,全权交给Java来做! JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Component public class User { private String name; public String getName () { return name; } @Value("lf") public void setName (String name) { this .name = name; } @Override public String toString () { return "User{" + "name='" + name + '\'' + '}' ; } }
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Configuration @ComponentScan("com.kuang.pojo") @Import(KuangConfig2.class) public class KuangConfig { @Bean public User user () { return new User (); } }
测试类
1 2 3 4 5 6 7 8 9 10 public class MyTest { public static void main (String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext (KuangConfig.class); User user = context.getBean("user" , User.class); System.out.println(user.getName()); } }
这种纯Java的配置方式,在SpringBoot中随处可见!
代理模式 为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】
代理模式的分类:
静态代理 角色分析:
抽象角色:一般会使用接口或者抽象类来解决
真实角色:被代理的角色
代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
客户:访问代理对象的人!
代码步骤:
接口
1 2 3 4 public interface Rent { public void rent () ; }
真实角色
1 2 3 4 5 6 public class Host implements Rent { public void rent () { System.out.println("房东出租房子!" ); } }
代理角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Proxy implements Rent { private Host host; public Proxy () {} public Proxy (Host host) { this .host = host; } public void rent () { host.rent(); seeHouse(); sign(); fee(); } public void seeHouse () { System.out.println("中介带着看房子!" ); } public void sign () { System.out.println("和中介签署租赁合同!" ); } public void fee () { System.out.println("中介收取费用!" ); } }
客户端访问代理角色
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Client { public static void main (String[] args) { Host host = new Host (); Proxy proxy = new Proxy (host); proxy.rent(); } }
代理模式的好处:
可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
公共角色就交给代理角色!实现了业务的分工!
公共业务发生扩展的时候,方便集中管理!
缺点:
一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低~
AOP实现
动态代理
基于接口 — JDK动态代理【我们在这里使用】
基于类:cglib
java字节码实现:javassist
需要了解两个类:Proxy:代理;InvocationHandler:调用处理程序。
代码步骤:
1.接口(注册方法)
1 2 3 4 5 6 7 8 package lf;public interface UserService { public void add () ; public void delete () ; public void update () ; public void query () ; }
2.真实角色(类似于房东—代理对象类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package lf;public class UserServiceImpl implements UserService { public void add () { System.out.println("增加了一个用户!" ); } public void delete () { System.out.println("删除了一个用户!" ); } public void update () { System.out.println("修改了一个用户!" ); } public void query () { System.out.println("查询了一个用户!" ); } }
3.动态代理工具类(不要变,直接用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package lf;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ProxyInvocationHandler implements InvocationHandler { private Object target; public void setTarget (Object target) { this .target = target; } public Object getProxy () { return Proxy.newProxyInstance(this .getClass().getClassLoader(), target.getClass().getInterfaces(), this ); } public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { log(method.getName()); Object invoke = method.invoke(target, args); return invoke; } public void log (String msg) { System.out.println("执行了" + msg + "方法" ); } }
4.客户端(类似于租客找代理的人)测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package lf;public class client { public static void main (String[] args) { UserServiceImpl userService = new UserServiceImpl (); ProxyInvocationHandler pio = new ProxyInvocationHandler (); pio.setTarget(userService); UserService proxy = (UserService)pio.getProxy(); proxy.add(); } }
动态代理的好处:
可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
公共角色就交给代理角色!实现了业务的分工!
公共业务发生扩展的时候,方便集中管理!
==一个动态代理类代理的是一个接口,一般就是对应的一类业务 ==
==一个动态代理类可以代理多个类,只要是实现了同一个接口即可!(类似于代理多个租客,就是能让多个客户都能实现接口方法不用每个用户都要实现接口来重写方法减少了代码量) ==
AOP ==提供声明式事务;允许用户自定义切面(在service业务增加新的事务) ==
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率 。
横切关注点:跨越应用程序多个模块的方法或功能。即与我们的业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等。。。
切面(ASPECT):横切关注点 被模块化的特殊对象。即 它是一个类
通知(Advice):切面必须要完成的工作,即 他是类中的一个方法
目标(target):被通知的对象
代理(Proxy):向目标对象应用通知之后创建的对象
切入点(PointCut):切面通知 执行的”地点”的定义
连接点(jointPoint):与切入点匹配的执行点
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
使用Spring实现AOP 【重点】使用AOP织入,需要导入一个依赖包!
1 2 3 4 5 <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.4</version > </dependency >
方式一:使用Spring的API接口 eg:在执行UserService实现类的所有方法时,增加日志功能
UserServer接口
1 2 3 4 5 6 7 package com.yang.service;public interface UserService { public void add () ; public void update () ; public void delete () ; public void select () ; }
UserServer实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.yang.service;public class UserServiceImpl implements UserService { public void add () { System.out.println("增加了一个用户" ); } public void update () { System.out.println("更新了一个用户" ); } public void delete () { System.out.println("删除了一个用户" ); } public void select () { System.out.println("检索了一个用户" ); } }
log类(不要导错包)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.yang.log;import org.springframework.aop.AfterReturningAdvice;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class log implements MethodBeforeAdvice , AfterReturningAdvice { public void before (Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了" ); } public void afterReturning (Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了" + method.getName() + "方法,返回值为" + returnValue); } }
配置文件(bean.xml)
==*切入点:execution:表达式,execution( (修饰词) *(返回值) *(类名) (方法名) (参数)) ..任意参数 ==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="userService" class ="com.yang.service.userserviceimpl" /> <bean id ="log" class ="com.yang.log.log" /> <bean id ="afterlog" class ="com.yang.log.afterlog" /> <aop:config > <aop:pointcut id ="poincut" expression ="execution(* com.yang.service.userserviceimpl.*(..))" /> <aop:advisor advice-ref ="log" pointcut-ref ="poincut" /> <aop:advisor advice-ref ="afterlog" pointcut-ref ="poincut" /> </aop:config > </beans >
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 import com.yang.service.userservice;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class test { public static void main (String[] args) { ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext ("bean.xml" ); userservice userService = classPathXmlApplicationContext.getBean("userService" , userservice.class); userService.add(); } }
方式二:自定义来实现AOP【主要是切面定义】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?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" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="userService" class ="com.yang.service.UserServiceImpl" /> <bean id ="log" class ="com.yang.log.Log" /> <bean id ="diy" class ="com.yang.diy.DiyPointCut" /> <aop:config > <aop:aspect ref ="diy" > <aop:pointcut id ="point" expression ="execution(* com.yang.service.UserServiceImpl.*(..))" /> <aop:before method ="beforeMethod" pointcut-ref ="point" /> </aop:aspect > </aop:config > </beans >
在diy包下定义自己的DiyPointCut切入类
1 2 3 4 5 6 7 8 9 public class DiyPointCut { public void before () { System.out.println("======方法执行前======" ); } public void after () { System.out.println("======方法执行后======" ); } }
方式三:使用注解实现AOP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?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" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="userService" class ="com.yang.service.UserServiceImpl" /> <bean id ="annotationPointCut" class ="com.yang.diy.AnnotationPointcut" /> <aop:aspectj-autoproxy /> </beans >
在diy包下定义注解实现的AnnotationPointCut增强类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.yang.diy;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspect public class AnnotationPointcut { @Before("execution(* com.yang.service.UserServiceImpl.*(..))") public void before () { System.out.println("====方法执行前====" ); } @After("execution(* com.yang.service.UserServiceImpl.*(..))") public void after () { System.out.println("====方法执行后====" ); } @Around("execution(* com.yang.service.UserServiceImpl.*(..))") public void around (ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前" ); Signature signature = pjp.getSignature(); System.out.println("signature" + signature); Object proceed = pjp.proceed(); System.out.println("环绕后" ); } }
整合Mybatis 方式一 步骤:
导入相关jar包**(注意版本相同且不重复不然会有大麻烦)**
junit
mybatis
mysql数据库
spring相关
aop织入器
mybatis-spring整合包【重点】在此还导入了lombok包。
配置Maven静态资源过滤问题!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <parent > <artifactId > spring</artifactId > <groupId > org.example</groupId > <version > 1.0-SNAPSHOT</version > </parent > <modelVersion > 4.0.0</modelVersion > <artifactId > springmabatis</artifactId > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-core</artifactId > <version > 5.3.1</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.6</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.3.1</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.2.0.RELEASE</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.4</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis-spring</artifactId > <version > 2.0.2</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.10</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.27</version > </dependency > </dependencies > <properties > <maven.compiler.source > 17</maven.compiler.source > <maven.compiler.target > 17</maven.compiler.target > </properties > <build > <resources > <resource > <directory > src/main/java</directory > <includes > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > </resources > </build > </project >
applicationContext.xml(放注册bean的id–供测试类调用的xml)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <import resource ="spring-dao.xml" /> <bean id ="userMapper" class ="com.yang.mapper.usermapperimpl" > <property name ="sqlSessionFactory" ref ="sqlSessionFactory" /> </bean > </beans >
mybatis-config.xml(放mybatis的一些配置–setting和别名)
1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <typeAliases > <package name ="com.yang.entity" /> </typeAliases > </configuration >
spring-dao.xml(放连接数据源的代码变动少)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?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" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="com.mysql.cj.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/day2021_9_6_studymybatis_db?useSSL=false& useUnicode=false& characterEncoding=utf-8" /> <property name ="username" value ="root" /> <property name ="password" value ="lf3354824" /> </bean > <bean id ="sqlSessionFactory" class ="org.mybatis.spring.SqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" /> <property name ="configLocation" value ="classpath:mybatis-config.xml" /> <property name ="mapperLocations" value ="classpath:com/yang/mapper/*.xml" /> </bean > </beans >
方式二 dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起整合方式一 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看。
将我们上面写的UserMapperImpl修改一下
1 2 3 4 5 6 7 8 9 public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper { public List<User> selectUser () { return getSqlSession().getMapper(UserMapper.class).selectUser(); } }
注入到Spring配置文件中。
1 2 3 <bean id ="userMapper" class ="com.kuang.mapper.UserMapperImpl" > <property name ="sqlSessionFactory" ref ="sqlSessionFactory" /> </bean >
Spring中的事务管理 Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。
编程式事务管理
将事务管理代码嵌到业务方法中来控制事务的提交和回滚
缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码
声明式事务管理
一般情况下比编程式事务好用。
将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。
1.使用Spring管理事务,注意头文件的约束导入 : tx
==这是aop和tx的头文件全部整合 ==
1 2 3 4 5 6 7 8 9 10 11 12 13 <?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:tx ="http://www.springframework.org/schema/tx" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://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/tx http://www.springframework.org/schema/tx/spring-tx.xsd" ></beans >
2.JDBC事务
1 2 3 4 <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" /> </bean >
3.配置好事务管理器后我们需要去配置事务的通知
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <tx:advice id ="txAdvice" transaction-manager ="transactionManager" > <tx:attributes > <tx:method name ="add" propagation ="REQUIRED" /> <tx:method name ="delete" propagation ="REQUIRED" /> <tx:method name ="update" propagation ="REQUIRED" /> <tx:method name ="query" read-only ="true" /> <tx:method name ="*" propagation ="REQUIRED" /> </tx:attributes > </tx:advice >
spring事务传播特性: 事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。
就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!
4.配置AOP,导入aop的头文件
1 2 3 4 5 6 <aop:config > <aop:pointcut id ="txPointCut" expression ="execution(* com.kuang.mapper.*.*(..))" /> <aop:advisor advice-ref ="txAdvice" pointcut-ref ="txPointCut" /> </aop:config >
5.测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import com.yang.entity.user;import com.yang.mapper.usermapper;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.io.IOException;import java.io.InputStream;import java.util.List;public class test { @Test public void test () throws IOException { ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext ("applicationContext.xml" ); usermapper usermapper = (usermapper) classPathXmlApplicationContext.getBean("userMapper" ); List<user> userList = usermapper.selectUser(); for (user user : userList) { System.out.println(user); } } }