学习视频链接:https://www.bilibili.com/video/BV1WE411d7Dv
2002,首次推出了Spring的雏形:interface 21
Rod Johnson, Spring Framework创始人
Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!
SSH:struct2 + Spring + Hibernate
SSM:SpringMVC + Spring + Mybatis
官网文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#spring-core
官方下载地址:https://repo.spring.io/release/org/springframework/spring
Github:https://github.com/spring-projects/spring-framework
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.4.RELEASE</version> </dependency>总结:Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
现代化的Java开发,就是基于Spring的开发
Spring Boot: 一个快速开发的脚手架基于Spring Boot可以快速开发单个微服务约定大于配置 Spring Cloud 基于Spring Boot实现的大多数公司都基于SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring以及SpringMVC。
弊端:发展太久之后,违背原来的理念。配置繁琐,人称:“配置地狱”
1.UserDao接口
package com.wang.dao; public interface UserDao { void getUser(); }2.UserDaoImpl
package com.wang.dao; public class UserDaoImpl implements UserDao { public void getUser() { System.out.println("默认获取用户的数据"); } } package com.wang.dao; public class UserDaoMySQLImpl implements UserDao { @Override public void getUser() { System.out.println("MySQL获取数据"); } } package com.wang.dao; public class UserDaoOracleImpl implements UserDao { @Override public void getUser() { System.out.println("Oracle获取用户数据"); } }3.UserService 业务接口
package com.wang.service; public interface UserService { void getUser(); }4.UserServiceImpl 业务实现类
package com.wang.service; import com.wang.dao.UserDao; import com.wang.dao.UserDaoImpl; import com.wang.dao.UserDaoMySQLImpl; import com.wang.dao.UserDaoOracleImpl; public class UserServiceImpl implements UserService { //创建哪一种UserDao的实现类,在代码中写死了,如果需要变更则需要改变源代码 private UserDao userDao = new UserDaoMySQLImpl(); public void getUser() { userDao.getUser(); } }在之前的业务中,用户的需求可能会影响原来的代码,因为我们需要根据用户的需求修改源代码!如果程序代码量,修改一次的成本非常昂贵。
我们使用一个Set接口, 发生了革命性的变化!
public class UserServiceImpl implements UserService { private UserDao userDao; //利用set进行动态实现值的注入 public void setUserDao(UserDao userDao){ this.userDao = userDao; } public void getUser() { userDao.getUser(); } } 之前程序是主动创建对象!控制权在程序员手上,用户的每一个需求都要程序员修改源代码使用了set注入后,程序不再具有主动性,而是变成了被动接收对象,外部只要传入 一个任意的UserDao的实现类,就可以创建出对应的对象原来的是程序控制,变成了用户控制这种思想从本质上解决了问题,程序员不用主动地去管理对象的创建了。系统的耦合性大大降低,可以更加专注在业务的实现上。这是IoC的原型之前的设计思想
如今的设计思想
控制反转(inversion of control)是一种设计思想,DI(dependency injection)依赖注入是一种实现IoC的方式。
在没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖完全硬编码在程序中,对象的创建由自己控制,控制反转后将对象的创建转移到了第三方
IoC是Spring框架的核心内容,使用多种方式完美实现了IoC,可以使用XML配置,也可以使用注解。新版本的Spring也可以零配置实现IoC
Spring容器在初始化时先读取配置文件,根据配置文件或者元数据创建与组织对象存入容器,程序使用时再从IoC容器中取出所需要的对象
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者结合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的
控制反转是一种通过描述(XML或注解)并且通过第三方去生产或获取特定对象的方式,在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection)
pojo
package com.wang.pojo; public class Hello { private String name; @Override public String toString() { return "Hello{" + "name='" + name + '\'' + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } }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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--使用Spring创建对象,在Spring中这些都称为Bean--> <!--bean = 对象 Hello hello = new Hello(); id= 变量名 class= 要new的对象 property 相当于给对象中的属性设置一个值 --> <bean id="hello" class="com.wang.pojo.Hello"> <property name="name" value="String"/> </bean> </beans>test
import com.wang.pojo.Hello; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { //获取spring的上下文对象 ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); //我们的对象现在都在spring中管理,我们要使用直接取出来就可以 Hello hello = (Hello) context.getBean("hello"); System.out.println(hello.toString()); } }这个过程就要控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring创建的
反转: 程序本身不创建对象,而是变成被动的接收对象
依赖注入:就是利用set方法来进行注入
使用无参构造方法创建对象(默认实现)
使用有参方法创建对象 1.下标赋值
<?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 = "user" class="com.neil.pojo.User"> <constructor-arg index="0" value="neill"/> </bean> </beans>2.类型赋值(不建议使用,可能有多个属性有相同类型)
<?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 = "user" class="com.neil.pojo.User"> <constructor-arg type="java.lang.String" value="neill"/> </bean> </beans>3.直接通过参数名来命名
<?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 = "user" class="com.neil.pojo.User"> <constructor-arg name="name" value="neil"/> </bean> </beans>总结:在配置文件加载的时候,容器中管理的对象就已经初始化了
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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id = "user" class="com.neil.pojo.User"> <constructor-arg name="name" value="neil"/> </bean> <alias name="user" alias="user2"/> </beans>test:
import com.wang.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void Test(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml"); //使用别名获取对象 User user = (User) applicationContext.getBean("user2"); user.show(); } }一般用于团队开发使用,可以将多个配置文件合并成一个
在resource目录下创建bean1.xml在ApplicationContext.xml中import <import resource="bean1.xml"/>【环境搭建】
1.复杂类型
package com.wang.pojo; public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }2.真实测试对象
package com.wang.pojo; import java.util.*; public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> card; private Set<String> games; private Properties info; private String wife; @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address=" + address + ", books=" + Arrays.toString(books) + ", hobbys=" + hobbys + ", card=" + card + ", games=" + games + ", info=" + info + ", wife='" + wife + '\'' + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String[] getBooks() { return books; } public void setBooks(String[] books) { this.books = books; } public List<String> getHobbys() { return hobbys; } public void setHobbys(List<String> hobbys) { this.hobbys = hobbys; } public Map<String, String> getCard() { return card; } public void setCard(Map<String, String> card) { this.card = card; } public Set<String> getGames() { return games; } public void setGames(Set<String> games) { this.games = games; } public Properties getInfo() { return info; } public void setInfo(Properties info) { this.info = info; } public String getWife() { return wife; } public void setWife(String wife) { this.wife = wife; } }3.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--第一种,普通值注入,value--> <bean id="student" class="com.wang.pojo.Student"> <property name="name" value="王"/> </bean> </beans>5.测试类
import com.wang.pojo.Student; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.getName()); } }6.完善注入信息
<?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="address" class="com.wang.pojo.Address"> <property name="address" value="山东"/> </bean> <bean id="student" class="com.wang.pojo.Student"> <!--第一种,普通值注入,value--> <property name="name" value="王"/> <!--第二种,bean注入,使用ref--> <property name="address" ref="address"/> <!--第三种,数组注入--> <property name="books"> <array> <value>红楼梦</value> <value>水浒传</value> <value>三国演戏</value> <value>西游记</value> </array> </property> <!--第四种,list集合注入--> <property name="hobbys"> <list> <value>LOL</value> <value>CSGO</value> <value>敲代码</value> </list> </property> <!--第五种,map注入--> <property name="card"> <map> <entry key="身份证" value="1312313123123313"/> <entry key="银行卡" value="123213213213123321312312"/> </map> </property> <!--第六种,set注入--> <property name="games"> <set> <value>LOL</value> <value>CSGO</value> </set> </property> <!--第七种,null注入--> <property name="wife"> <null/> </property> <!--第八种,Properties注入--> <property name="info"> <props> <prop key="学号">20190525</prop> <prop key="性别">男</prop> <prop key="姓名">小明</prop> </props> </property> </bean> </beans>7.打印结果
Student{name='王', address=Address{address='山东'}, books=[红楼梦, 水浒传, 三国演戏, 西游记], hobbys=[LOL, CSGO, 敲代码], card={身份证=1312313123123313, 银行卡=123213213213123321312312}, games=[LOL, CSGO], info={学号=20190525, 性别=男, 姓名=小明}, wife='null'}我们可以使用p命名和c命名的方式注入,一个利用有参构造,一个利用无参构造
代码如下:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--p命名空间注入,可以直接注入属性的值:property--> <bean id="user" class="com.wang.pojo.User" p:age="29" p:name="123"/> <!--c命名空间注入,可以通过构造器注入:construct-args--> <bean id="user2" class="com.wang.pojo.User" c:name="wang" c:age="123"/> </beans>注意:在使用时需要加约束
xmlns:p="http://www.springframework.org/schema/p"//p命名约束 xmlns:c="http://www.springframework.org/schema/c"//c命名约束重点学习:
1.单例作用域(spring默认机制)
Only one shared instance of a singleton bean is managed, and all requests for beans with an ID or IDs that match that bean definition result in that one specific bean instance being returned by the Spring container.
To put it another way, when you define a bean definition and it is scoped as a singleton, the Spring IoC container creates exactly one instance of the object defined by that bean definition. This single instance is stored in a cache of such singleton beans, and all subsequent requests and references for that named bean return the cached object. The following image shows how the singleton scope works:
也可以显式增加作用域
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>2.原型模式
The non-singleton prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made. That is, the bean is injected into another bean or you request it through a getBean() method call on the container. As a rule, you should use the prototype scope for all stateful beans and the singleton scope for stateless beans.
The following diagram illustrates the Spring prototype scope:
每次从容器中getBean的时候都会产生一个新的对象
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>3.Request、Session、application
只在web开发中使用
pojo:
package com.wang.pojo; public class Cat { public void shout(){ System.out.println("miao"); } } package com.wang.pojo; public class Dog { public void shout(){ System.out.println("wang"); } } package com.wang.pojo; public class People { private Dog dog; private Cat cat; private String name; public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "People{" + "dog=" + dog + ", cat=" + cat + ", name='" + name + '\'' + '}'; } }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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="cat" class="com.neil.pojo.Cat"/> <bean id="dog" class="com.neil.pojo.Dog"/> <bean id="person" class="com.neil.pojo.Person"> <property name="name" value="Neil"/> <property name="dog" ref="dog"/> <property name="cat" ref="cat"/> </bean> </beans>test:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test1(){ ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); People people = context.getBean("people",People.class); people.getCat().shout(); people.getDog().shout(); } }小结:
byName:需要保证所有Bean的Id唯一,会自动在容器上下文中查找,和自己对象set方法属性对应名称相同的的bean IDbyType:需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一致Jdk1.5支持注解,spring2.5就支持注解了
要使用注解须知:
导入约束:context约束, beans头文件配置注解的支持 <?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/> <!--指定扫描的包,这个包下面的注解就会生效--> <context:component-scan base-package="com.wang.pojo"/> </beans> @Autowired:直接在属性上使用即可!也可以在set方法上使用。使用Autowired我们可以不用编写Set方法,前提是自动装配的属性在IoC(Spring)容器中存在,且装配方式为byType@Resource:默认按照ByName进行实现,找不到就用ByType@Autowired = (required = false):表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错。@Qualifier(value="dog"):表示显示注册哪个Bean,如果自动装配环境复杂,需要配合自动装配注解一起使用@Resource:是java的注解,用的比较少,效果等于以上两个的集合,可以使用@Resource(name = 'cat2')指定装配@Nullable:字段标记了这个注解,说明这个字段可以为null[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ssuQGEN4-1599130682121)(E:\狂神笔记和代码\spring5学习.assets\image-20200817175057087.png)]
@Resource和@Autowired的区别:
都是用来自动装配的,都可以放在属性字段上@Autowired通过ByType方式实现(常用)@Resource通过ByName方式实现,如果找不到通过ByType实现!如果两个都找不到的情况下就会报错(常用)执行顺序不同:@Autowired通过ByType方式实现,@Resource通过ByName方式实现,如果找不到通过ByType实现!如果两个都找不到的情况下就会报错在Spring4之后,要使用注解开发,必须要保证AOP的包已经导入
使用注解也需要context约束,增加注解的支持
<?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>@Component有几个衍射注解,我们在web开发中,会按照mvc三层架构分层,功能和@Component类似
Dao:使用@Repository注解Service:使用@Service注解controller:使用@Controller注解这四个注解功能是一样的,都是代表将某个类注册到Spring容器中,也就是装配Bean
xml与注解:
xml更加万能,适用于任何场景!维护更加方便注解不是自己的类使用不了,维护相对复杂xml与注解最佳实践:
xml用来管理Bean注解只用来负责管理属性的注入我们在使用过程中,只需要注意一个问题:如果必须让注解生效,就需要开启注解的支持 <!--开启注解的支持--> <context:annotation-config/> <!--指定扫描的包,这个包下面的注解就会生效--> <context:component-scan base-package="com.wang"/>我们现在要完全不使用Spring的Xml配置了,全权交给Java来做
JavaConfig是Spring的一个子项目,在Spring4之后,他成为了核心功能
1.配置Config类
package com.wang.config; import com.wang.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration //这个也会被spring接管,注册到容器中,因为它本来就是一个@Component, // @Configuration本来是一个@Conponent组件,代表这是一个配置类,和ApplicationContext.xml一样 @ComponentScan("com.wang.pojo") @Import(WangConfig2.class) public class WangConfig { /*注册的Bean,相当于我们之前写的Bean 这个方法的名字,相当于原来Bean标签的Id属性 这个方法的返回值,相当于bean标签中的class属性*/ @Bean public User getUser(){ return new User(); } }2.pojo
package com.wang.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; //这个注解的意思,就是说这个类被spring接管了,注册到了容器中 @Component public class User { private String name; public String getName() { return name; } @Value("小驴") public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }3.test
import com.wang.config.WangConfig; import com.wang.pojo.User; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class MyTest { public static void main(String[] args) { //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig上下位来获取容器,通过配置类的class对象加载 ApplicationContext context = new AnnotationConfigApplicationContext(WangConfig.class); User getUser = (User) context.getBean("getUser"); System.out.println(getUser.getName()); } }这种纯java的配置方式,在SpringBoot中随处可见!
为什么要学习代理模式?因为这是SpringAOP的底层!【SpringAOP和SpringMVC】
代理模式原型:
角色分析:
抽象角色:一般会使用接口或者抽象类来解决真实角色:被代理的角色代理角色:代理真实角色,代理真实角色后,我们会做一些附属操作客户:访问代理对象的人代码实现:
接口
public interface Rent{ public void rent(); }真实角色
public class Landlord implements Rent(){ public void rent(){ System.out.Println("我是房东我有个房子需要出租") } }代理角色
public class Proxy implements Rent(){ private Landlord landlord; public Proxy(){ } public Proxy(Landlord landlord){ this.landlord = landlord; } public void rent(){ inspection(); landlord.rent(); signContract(); getFee(); } public void insepection(){ System.out.println("中介带你看房子"); } public void signContract(){ System.out.println("和中介签合同了"); } public void getFee(){ System.out.println("收中介费"); } }客户端访问代理角色
public class Client(){ public static void main(String[] args){ Landlord landlord = new Landlord(); //房东注入到中介中 Proxy proxy = new Proxy(landlord); //你不用面对房东,直接找中介租房即可 proxy.rent(); } }代理模式的好处:
可以让真实角色的操作更加纯粹,不用关注公共业务公共业务交给了代理角色,实现了业务分工公共业务发生扩展的时候,方便集中管理缺点:
一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低需要了解两个类:
Proxy:调用其静态方法newProxyInstance()生成实例InvocationHandler:调用处理程序InvocationHandler:java.lang.reflect下的一个接口
动态代理类: public class ProxyInvocationHandler implements InvocationHandler{ //被代理的接口 private Object target; public void setTarget(Object target){ this.target = target; } //生成得到代理类 public Object getProxy(){ //需要传入类加载器,代理的接口和InvocationHandler return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method,Object[] args) throw Throwable{ log(method.getName()); Object result = method.invoke(target, args); return result; } public void log(String msg){ System.out.Println("执行了" + msg + "方法"); } }2.实现类:
public class Client{ public static void main(String[] args){ //真实角色 UserServiceImpl userService = new UserServiceImpl(); //代理角色,不存在 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //设置要代理的对象 pih.setTarget(userService); //动态生成代理类 UserService proxy = (UserSErvice)pih.getProxy(); proxy.query(); } }动态代理的好处:
一个动态代理类代理的是一个接口,一般对应的就是一类业务一个动态代理类可以多个类,子要是实现了同一个接口AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译的方式和运行期间动态代理实现程序功能的同一维护的一种技术。AOP是OOP的延续,是软件开发的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范性。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
提供声明式事务:允许用户自定义切面
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等切面(Asepct):横切关注点被模块化的特殊对象。即,它是一个类。通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。目标(Target):被通知的对象。代理(Proxy):向目标对象应用通知之后创建的对象。切入点(PointCut):切面通知执行的 “地点”的定义。连接点(JointPoint):与切面点匹配的执行点 。SpringAOP中,通过Advice定义的横切逻辑,Spring中支持5中类型的Advice:
即Aop在不改变原有代码的情况下,去增加新的功能。
环境配置:
使用AOP织入,需要导入一个依赖包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>1.编写UserService和UserServiceImpl
package com.wang.service; public interface UserService { public void add(); public void delete(); public void update(); public void select(); } package com.wang.service; 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 select() { System.out.println("查询了一个用户"); } }2.编写log类
package com.wang.log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class Log implements MethodBeforeAdvice { //method:要执行的目标对象的方法 //args:参数 //target:目标对象 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了"); } } package com.wang.log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class AfterLog implements AfterReturningAdvice { //returnValue:返回值 public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("执行了" + method.getName() + "方法,并且返回了" + returnValue); } }3.编写配置文件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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--注册bean--> <bean id="userService" class="com.wang.service.UserServiceImpl"/> <bean id="log" class="com.wang.log.Log"/> <bean id="afterLog" class="com.wang.log.AfterLog"/> <!--方法一:使用原生的Spring API接口--> <!--配置AOP:需要导入AOP约束--> <aop:config> <!--切入点 expression表达式 execution(要执行的位置)--> <aop:pointcut id="pointcut" expression="execution(* com.wang.service.UserServiceImpl.*(..))"/> <!--执行环绕增加--> <aop:advisor advice-ref="log" pointcut-ref="pointcut" /> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>4.编写测试类
import com.wang.service.UserService; import com.wang.service.UserServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); //动态代理代理的是接口 UserService userService = (UserService) context.getBean("userService"); userService.add(); } }测试结果:
execution 完整参数:(修饰符 返回值 包名.类名/接口名.方法名(参数列表))
(…)可以代表所有参数,()代表一个参数,(,String)代表第一个参数为任何值,第二个参数为String类型.
1.自定义类
package com.wang.diy; public class DiyPointCut { public void before(){ System.out.println("方法执行前"); } public void after(){ System.out.println("方法执行后"); } }2.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--注册bean--> <bean id="userService" class="com.wang.service.UserServiceImpl"/> <bean id="log" class="com.wang.log.Log"/> <bean id="afterLog" class="com.wang.log.AfterLog"/> <!--方法二:自定义类--> <bean id="diy" class="com.wang.diy.DiyPointCut"/> <aop:config> <!--自定义切面 ref要引用的类--> <aop:aspect ref="diy"> <!--切入点--> <aop:pointcut id="point" expression="execution(* com.wang.service.UserServiceImpl.*(..))"/> <!--通知--> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config> </beans>1.新建一个注解类
//方法三:使用注解方式实现AOP @Aspect //标注这个类是一个切面 public class AnnotationPointCut { @Before("execution(* com.wang.service.UserServiceImpl.*(..))") public void before(){ System.out.println("方法执行前"); } @After("execution(* com.wang.service.UserServiceImpl.*(..))") public void after(){ System.out.println("方法执行后"); } //在环绕增强中,我们可以给定一个参数,到表我们要获取切入的点 @Around("execution(* com.wang.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("Annotation环绕前"); System.out.println("获得签名" + joinPoint.getSignature()); //执行方法 Object proceed = joinPoint.proceed(); System.out.println("Annotation环绕后"); } }2.ApplicationContext.xml注册Bean并且开启注解
<!--方法三--> <bean id="annotationpointcut" class="com.wang.diy.AnnotationPointCut"/> <!--开启注解支持--> <aop:aspectj-autoproxy/>步骤:
导入相关jar包
1.Junit
2.MyBatis
3.MySQL数据库
4.Spring相关
5.AOP织入
6.MyBatis-spring【新包】
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.4</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.0.2.RELEASE</version> </dependency> <!--spring操作数据库的话,还需要的一个spring-jdbc的依赖--> <dependency> <groupId>org,springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.0</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> </dependencies> 7.编写配置文件
8.测试
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException。最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
1.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--DataSource:使用Spring的数据源替换MyBatis的配置 c3p0 dbcp druid--> <!--这里使用Spring提供的jdbc--> <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:3307/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <!--sqlSessionFactory 可以替换所有的mybatis-config.xml里面的配置--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!--绑定MyBatis配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/wang/mapper/*.xml"/> </bean> <!--SqlSessionTemplate:就是我们使用的sqlSession--> <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <!--只能使用构造器注入sqlSessionFactory,因为它没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> <bean id="userMapperImpl" class="com.wang.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSessionTemplate"/> </bean> <bean id="userMapperImpl2" class="com.wang.mapper.UserMapperImpl2"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> </beans>2.UserMapper
public interface UserMapper { public List<User> selectUser(); }3.UserMapperImpl
public class UserMapperImpl implements UserMapper { //我们的所有操作与拿来都使用sqlSession //现在我们都使用sqlSessionTemplete private SqlSessionTemplate sqlSession; private SqlSessionTemplate sqlSessiont; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } public List<User> selectUser() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); } public void setSqlSessiont(SqlSessionTemplate sqlSessiont) { this.sqlSessiont = sqlSessiont; } public SqlSessionTemplate getSqlSessiont() { return sqlSessiont; } }UserMapperImpl2
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper { public List<User> selectUser() { SqlSession sqlSession = getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); } }4.UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:绑定一个对应的Dao(Mapper)接口--> <mapper namespace="com.wang.mapper.UserMapper"> <!--id对应对应Mapper接口的方法名字--> <select id="selectUser" resultType="com.wang.pojo.User" resultMap="UserMap"> select * from mybatis.user </select> <resultMap id="UserMap" type="com.wang.pojo.User"> <!--column表示字段,property表示实体类属性--> <result column="id" property="id"/> <result column="name" property="name"/> <result column="pwd" property="pwd"/> </resultMap> </mapper>5.测试
public class MyTest { @Test public void test() throws IOException { String resource = "mybatis-config.xml"; InputStream in = Resources.getResourceAsStream(resource); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = build.openSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.selectUser(); for (User user : userList) { System.out.println(user); } } @Test public void test2(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml"); UserMapper userMapper = applicationContext.getBean("userMapperImpl2", UserMapper.class); for (User user : userMapper.selectUser()) { System.out.println("user = " + user); } } }1.ApplicationContext.xml
<!--创建声明事务管理对象--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean> <!--结合AOP实现声明式事务织入--> <!--配置事务通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--给哪些方法配置事务--> <!--配置事务的传播特性 propagation--> <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> <!--配置事务切入--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.neil.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>2.UserMapperImpl
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper { //我们的所有操作与拿来都使用sqlSession @Override public List<User> selectUser() { User user = new User(6, "张三三", "123123"); UserMapper mapper = getSqlSession().getMapper(UserMapper.class); mapper.addUser(user); mapper.deleteUser(6); return mapper.selectUser(); } @Override public int addUser(User user) { return getSqlSession().getMapper(UserMapper.class).addUser(user); } @Override public int deleteUser(int id) { return getSqlSession().getMapper(UserMapper.class).deleteUser(id); } }正确性。防止数据损坏
持久性:事务一旦完成,无论系统发生如何变故,结果都不会再被影响,被持久化写到存储器中[外链图片转存中…(img-MMkLP9Bm-1599130682146)]
1.ApplicationContext.xml
<!--创建声明事务管理对象--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean> <!--结合AOP实现声明式事务织入--> <!--配置事务通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--给哪些方法配置事务--> <!--配置事务的传播特性 propagation--> <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> <!--配置事务切入--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.neil.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>2.UserMapperImpl
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper { //我们的所有操作与拿来都使用sqlSession @Override public List<User> selectUser() { User user = new User(6, "张三三", "123123"); UserMapper mapper = getSqlSession().getMapper(UserMapper.class); mapper.addUser(user); mapper.deleteUser(6); return mapper.selectUser(); } @Override public int addUser(User user) { return getSqlSession().getMapper(UserMapper.class).addUser(user); } @Override public int deleteUser(int id) { return getSqlSession().getMapper(UserMapper.class).deleteUser(id); } }