Spring-Learn
1、Spring
1.1、简介
- Spring:春天------>给Java开发带来了春天;
- 2002,首次推出Spring框架的雏形:interface21框架!
- Spring框架以interface21框架为基础,经过设计,不断丰富,于2004连3月24日发布了1.0正式版。
- Spring Framework创始人,著名作者。
- Spring:使现有的技术更加容易使用,本身是一一个大杂烩,整合了现有的技术框架。
- SSH:Struct2+Spring+Hibernate
- SSM:SpringMvc+Spring+Mybatis
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
1.2、优点
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级的、非入侵的框架
- 控制反转(ioc),面写切面编程(aop)
- 支持事务的处理对框架整合的支持
总结Spring是一个轻量级的控制反转和面向切面编程的框架
1.3、组成
1.4、扩展
现代化的Java开发:基于“Spring”的开发
- springboot
- springcloud
2、IOC(控制反转)
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
3、HelloSpring
3.1、创建一个maven项目(导入jar包)
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
3.2、编写代码
1.编写一个实体类:
package com.chen.pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" + "str='" + str + '\'' + '}';
}
}
2.编写spring文件,命名为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 id="hello" class="com.chen.pojo.Hello">
<property name="str" value="Spring"></property>
</bean>
</beans>
3.测试类:
package com.chen.pojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloTest {
public static void main(String[] args) {
// 实例化容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中取bean
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
//hello.toString();
}
}
4、IoC创建对象的方式
4.1、使用无参构造创建对象(默认方式)
4.2、若使用有参构造创建对象
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.hecn.pojo.User">
<constructor-arg index="0" value="000000">
</constructor-arg>
</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="user2" class="com.hecn.pojo.User">
<constructor-arg type="java.lang.String" value="参数类型"/>
</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="user3" class="com.hecn.pojo.User">
<constructor-arg name="name" value="name"/>
</bean>
</beans>
总结:
在配置文件加载的时候,容器中管理的对象就已经初始化了!
5、Spring配置
5.1、Alias(别名)
<!--别名 name:原有名;
alias:别名。
-->
<alias name="user" alias="user4"/>
5.2、Bean的配置
<!--
id:bean的唯一标识符,
class:bean对象所对应的全限定名:包名+类型
name:也是别名,而且name可以取多个别名
-->
<bean id="userT1" class="com.hecn.pojo.UserT" name="u2,u3 u4;u5">
<property name="name" value="hehe"/>
</bean>
5.3、import
<!--
一般用于团队开发,它可以将多个配置文件,导入并合并为一个总的配置
-->
<import resource="applicationContext2.xml"/>
<import resource="applicationContext3.xml"/>
6、依赖注入
6.1、构造器注入
见4.2
6.2、Set方式注入
- 依赖注入:set注入
- 依赖:bean对象的创建依赖于容器!
- 注入:bean对象中的所有属性,由容器来注入!
【环境搭建】
1.复杂类型
package com.hecn.pojo;
public class Adress {
private String adress;
public Adress(String adress) {
this.adress = adress;
}
public String getAdress() {
return adress;
}
public void setAdress(String adress) {
this.adress = adress;
}
}
- 真实测试对象
package com.hecn.pojo;
import lombok.Data;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/** @author 24497 */
@Data // 使用了lombook插件
public class Student {
// 字符串
private String name;
// 引用
private Adress adress;
// 集合
private String[] books;
// 列表
private List<String> hobbys;
// Map
private Map<String, String> card;
// Set
private Set<String> games;
// Null
private String wife;
// Props
private Properties info;
}
3.beans.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="student" class="com.hecn.pojo.Student">
<!--普通值注入 -->
<property name="name" value="张三"></property>
<!--<property name="adress" ref=""></property>-->
</bean>
</beans>
4.测试类
package com.hecn.pojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StudentTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.getName());
}
}
5.完善注入信息
<?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.hecn.pojo.Address">
<property name="address" value="荆州"/>
</bean>
<bean id="student" class="com.hecn.pojo.Student">
<!--1.普通值注入,value -->
<property name="name" value="张三"></property>
<!--2.Bean注入,ref -->
<property name="address" ref="address"></property>
<!--3.数组注入, -->
<property name="books">
<array>
<value>红楼梦</value>
<value>三国演义</value>
<value>西游记</value>
<value>水浒传</value>
</array>
</property>
<!--4.List注入, -->
<property name="hobbys">
<list>
<value>听歌</value>
<value>敲代码</value>
<value>学英语</value>
</list>
</property>
<!--4.List注入, -->
<property name="card">
<map>
<entry key="身份证" value="123456"/>
<entry key="银行卡" value="23455667"/>
</map>
</property>
<!--5.Set注入, -->
<property name="games">
<set>
<value>LOL</value>
<value>OW</value>
<value>饥荒</value>
</set>
</property>
<!--6.空字符串, -->
<property name="gongzi" value=""/>
<!--7.null, -->
<property name="wife">
<null/>
</property>
<!--8.Properties, -->
<property name="info">
<props>
<prop key="name">陈</prop>
<prop key="no">201640450114</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
6.3、其他方式(拓展方式)
我们可以使用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">
<bean id="user" class="com.hecn.pojo.User">
<property name="name" value="张三"/>
<property name="age" value="24"/>
</bean>
<bean id="user2" class="com.hecn.pojo.User">
<constructor-arg name="name" value="狄仁杰"></constructor-arg>
<constructor-arg name="age" value="21"></constructor-arg>
</bean>
<!--p命名空间,可以直接注入属性的值:property -->
<bean id="userP" class="com.hecn.pojo.User" p:name="李四" p:age="26"/>
<!--c命名空间,通过构造器注入:construct-args -->
<bean id="userC" class="com.hecn.pojo.User" c:name="\(@^0^@)/" c:age="21"/>
</beans>
- 测试:
package com.hecn.pojo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.*;
public class StudentTest {
@Test
public void MyTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user2");
//User userP = context.getBean("user3", User.class);
//User userC = context.getBean("user3", User.class);
System.out.println(user2.toString());
System.out.println(user.toString());
//System.out.println(userP.toString());
//System.out.println(userC.toString());
}
}
- 注意点:p,c命名空间需要导入xml约束才能使用
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
6.4、Bean的作用域(Bean)
Scope | Description |
---|---|
singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
prototype | Scopes a single bean definition to any number of object instances. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
1.singleton单例模式(Spring默认机制)
<bean id="userP" class="com.hecn.pojo.User" p:name="李四" p:age="26" scope="singleton"/>
2.prototype原型模式:每次从容器中get的时候都会产生一个新对象
<bean id="userC" class="com.hecn.pojo.User" c:name="\(@^0^@)/" c:age="21" scope="prototype"/>
3.其余的request,session,application这些只能在web开发中使用到!
7、Bean的自动装配
- Bean的自动装配是Spring满足bean依赖的一种方式
- Spring会在上下文中自动寻找,并自动给bean装配属性
在spring中有三种自动装配的方式
1.在xml显示的配置
2.在Java中显示配置
3.隐示的自动装配【重要】
7.1、测试
环境搭建:一个人有两个宠物
7.2、ByName自动装配
<!--ByName自动装配
会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid-->
<bean id="peopleAutowire" class="com.hecn.pojo.People" autowire="byName">
<property name="name" value="李四"/>
</bean>
7.3、ByType自动装配
<!--ByName自动装配
会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid
ByType:会自动在容器上下文查找,和自己对象属性类型相同的bean
-->
<bean id="peopleAutowire" class="com.hecn.pojo.People" autowire="byType">
<property name="name" value="李四"/>
</bean>
小结:
-
byName的时候需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set方法一致
-
byType的时候需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
7.4、使用注解实现自动装配
jdk1.5支持的注解,Spring2.5就支持注解了
我们使用注解须知:
-
导入约束:context的约束 xmlns:context=“http://www.springframework.org/schema/context”
-
配置注解的支持:context:annotation-config/
<?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
-
它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。
-
@Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合**@Qualifier**使用;
-
@Autowired @Qualifier=byType||byName
-
@Autowired(required = false)处理空指针
@Autowired
private Cat cat;
@Resource
@Resource(name = "dog11")
private Dog dog;
小结:
- @Resource和**@Autowired**注解都是用来自动装配的;
- @AutoWried按by type自动装配;
- @Resource默认按byName装配,找不到这通过byType实现;
它们的主要区别就是@Autowired是默认按照类型装配的 @Resource默认是按照名称装配的
8、使用注解开发
- 在spring4之后,想要使用注解形式,必须得要引入aop的包
Maven: org.springframework:spring- aop:4.3.9.RELEASE
- 在配置文件当中,还得要引入一个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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
8.1、bean
8.2、属性如何注入
package com.hecn.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/** @author 24497 */
@Component // 等价于<bean id="user" class="com.hecn.pojo.User"/>
public class User {
/** 相当于<property name="name" value="\(@^0^@)/"/> */
@Value("\\(@^0^@)/")
public String name;
}
8.3、衍生的注解
@Component(组件)有几个衍生的注解
- dao【@Repository】
- service【@Service】
- controller【@Controller】
这四个功能一样,把某个类注册到Spring容器中,准备bean。
8.4、自动装配置
8.5、作用域
@Component // 等价于<bean id="user" class="com.hecn.pojo.User"/>
@Scope("singleton")
public class User {
/** 相当于<property name="name" value="\(@^0^@)/"/> */
@Value("\\(@^0^@)/")
public String name;
}
8.6、小结
xml与注解:
- xml万能,适用于任何场所,维护简单方便
- 注解不是自己的类使用不了,维护相对复杂
xml与注解的最佳实践:
- xml用来管理bean;
- 注解只负责完成属性的注入;
- 我们在使用的过程中,只需要注意一个问题: 必须让注解生效,就需要开启注解的支持
<context:component-scan base-package="xx.xx.xx"/>
<context:annotation-config/>
9、使用Java的方式配置spring
我们现在要完全不使用Spring的xml配置了,全权交给Java来做!
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!
实体类:
package com.hecn.pojo;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
@Getter
@Setter
@ToString
public class User {
@Value("李四")
private String name;
}
配置类:
package com.hecn.config;
import com.hecn.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
/** 等价于applicationContext.xml */
@ComponentScan("com.hecn.pojo")
@Import(HecnConfig2.class)
public class HecnConfig {
/**
* @return 1.注册一个bean,相当于bean标签<bean><bean/>; 2.方法名相当于<bean id="user"></bean>中的id属性; 3.返回值相当于<bean
* class="com.hecn.pojo.User"></bean>中的class属性。
*/
@Bean
public User user() {
// 就是返回要注入到bean的对象
return new User();
}
}
测试类:
import com.hecn.config.HecnConfig;
import com.hecn.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
@Test
public void myTest() {
ApplicationContext context = new AnnotationConfigApplicationContext(HecnConfig.class);
User user = context.getBean("user", User.class);
System.out.println(user.toString());
}
}
这种纯Java的配置方式,在SpringBoot中随处可见!
10、代理模式
why?因为这就是SpringAOP的底层!
代理模式的分类:代理模式分为静态代理、动态代理。
-
静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
-
动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
10.1、静态代理
角色分析:
-
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
-
真实角色:被代理的角色,实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
-
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
-
客户:访问代理对象的人
优点:
- 可以使真实角色的操作更纯粹,不用去关注共工业务;
- 公共也就就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实角色就会产生一 个代理角色;代码量会翻倍~ 开发效率会变低 ~
代码实现:
- 抽象角色:Rent.java
package com.hecn.demo;
/**
* 租房
* @author 24497
*/
public interface Rent {
public void rent();
}
- 真实角色:Host.java
package com.hecn.demo;
/** @author 24497 */
public class Host implements Rent {
public void rent() {
System.out.println("房东要出租房子");
}
}
- 代理角色:Proxy.java
package com.hecn.demo01;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/** 中介 @author 24497 */
@NoArgsConstructor
@AllArgsConstructor
public class Proxy implements Rent {
private Host host;
public void rent() {
host.rent();
}
// 看房
public void seeHouse() {
System.out.println("带你看房");
}
// 收取中介费用
public void fare() {
System.out.println("收取中介费");
}
// 合同
public void hetong() {
System.out.println("合同");
}
}
- 客户:Client.java
package com.hecn.demo01;
/**
* 客户
*
* @author 24497
*/
public class Client {
public static void main(String[] args) {
// 房东
Host host = new Host();
// 代理==中介,附属操作
Proxy proxy = new Proxy(host);
// 直接找中介
proxy.rent();
}
}
10.2、动态代理
- 动态代理的角色和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理–cglib
- 现在用的比较多的是 javasist 来生成动态代理 .
- 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!
Proxy:代理,InvocationHandler:调用处理程序
InvocationHandler
动态代理的好处
静态代理有的它都有,静态代理没有的,它也有!
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
- 一个动态代理 , 一般代理某一类业务
- 一个动态代理可以代理多个类,代理的是接口!
11、AOP
11.1、什么是aop?
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
11.2、aop在Spring中的作用
提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
通知类型 | 连接点 | 实现接口 |
---|---|---|
前置通知Before | 方法前 | org.springframework.aop.MethodBeforeAdvice |
后置通知After | 方法后 | org.springframework.aop.AfterReturningAdvice |
环绕通知Around | 方法前后 | org.aopalliance.intercept.MethodInterceptor |
异常通知After-throwing | 方法抛出异常 | org.springframework.aop.ThrowsAdvice |
返回通知After-returning | 方法成功后 | org.springframework.aop.IntroductionInterceptor |
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 。
11.3、使用spring实现aop
【重点】使用AOP织入,需要导入一个依赖包!
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一:使用SpringAPI借口实现
- 编写业务接口类和实现类(UserService.interface)
package com.hecn.service;
/**
* @author 24497
*/
public interface UserService {
public void add();
public void delete();
public void update();
public void selete();
}
package com.hecn.service;
/**
* @author 24497
*/
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 selete() {
System.out.println("查询了一个用户");
}
}
- 增强类,
package com.hecn.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/** 前置增强 @author 24497 */
public class Log implements MethodBeforeAdvice {
/**
* @param method 要执行目标对象的方法
* @param args 参数
* @param target 目标对象
* @throws Throwable
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了。");
}
}
package com.hecn.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* 方法执行成功后返回通知
* 后置方法
* @author 24497
*/
public class AfterLog implements AfterReturningAdvice {
/**
*
* @param returnValue 执行方法后返回的值(如果有)
* @param method 被调用的方法
* @param args 参数
* @param target target of the method invocation. May be {@code null}.
* @throws Throwable if this object wishes to abort the call. Any exception thrown will be
* returned to the caller if it's allowed by the method signature. Otherwise the exception
* will be wrapped as a runtime exception.
*/
public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
throws Throwable {
System.out.println("执行了" + method.getName() + "方法,返回结果为" + returnValue);
}
}
- bean的注册以及aop的配置(注意导入约束)
<?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.hecn.service.UserServiceImpl"/>
<bean id="log" class="com.hecn.log.Log"></bean>
<bean id="afterLog" class="com.hecn.log.AfterLog"></bean>
<!-- 方式一:使用原生的apringapi接口-->
<!-- 配置aop:需要导入aop约束-->
<aop:config>
<!--切入点: expression:表达式,execution(要执行的位置!) For example : 'execution(* com.xyz.myapp.service.*.*(..))'-->
<aop:pointcut id="pointcut" expression="execution(* com.hecn.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
- 测试
import com.hecn.service.UserService;
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();
}
}
Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .
Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .
方式二:自定义来实现(切面定义)
目标业务类不变依旧是userServiceImpl
- 写我们自己的一个切入类:diyPointCut.java
package com.hecn.diy;
public class DiyPiontCut {
public void before() {
System.out.println("===============方法执行前===============");
}
public void after() {
System.out.println("===============方法执行后===============");
}
}
- spring配置aop配置
<?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.hecn.service.UserServiceImpl"/>
<bean id="log" class="com.hecn.log.Log"></bean>
<bean id="afterLog" class="com.hecn.log.AfterLog"></bean>
<bean id="diy" class="com.hecn.diy.DiyPiontCut"/>
<!--方式二:自定义类-->
<aop:config>
<!-- 自定义切面,ref:引用类-->
<aop:aspect ref="diy">
<!-- 切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.hecn.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
- 测试
import com.hecn.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void tset() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
方式三:使用注解实现
- 编写一个注解实现的增强类
package com.hecn.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;
/** @author 24497 */
@Aspect
public class AnnotationPointCut {
@Before(value = "execution(* com.hecn.service.UserServiceImpl.*(..))")
public void before() {
System.out.println("=====方法执行前=====");
}
@After("execution(* com.hecn.service.UserServiceImpl.*(..))")
public void after() {
System.out.println("=====方法执行后=====");
}
@Around(value = "execution(* com.hecn.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
// 获得签名
Signature signature = jp.getSignature();
System.out.println("signature:" + signature);
// 执行方法
Object proceed = jp.proceed();
System.out.println("环绕后");
}
}
- 在Spring配置文件中,注册bean,并增加支持注解的配置
<!--第三种方式:注解实现-->
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
12、整合Mybatis
步骤:
-
导入jar包
- junit 单元测试
<!--1.junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency>
- mybatis
<!--2.mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency>
- mysql数据库
<!--3.mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency>
- spring相关的
<!--4.spring相关--> <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>
- aop织入
<!--5.aop织入--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency>
- mybatis-spring
<!--6.mybatis-spring--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.3</version> </dependency>
-
编写数据源配置:
<!--1.DataSource:
正在使用 Spring + MyBatis,则没有必要在mybatis配置(mybatis-config)里配置事务管理器,
因为 Spring 模块会使用自带的管理器来覆盖前面mybatis的配置。
我们这里使用spring提供的jdbc:
org.springframework.jdbc.datasource.DriverManagerDataSource
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&characterEncoding=utf-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</bean>
- sqlSessionFactory:
<!--2.sqlSessionFactory:
SqlSessionFactory 有一个唯一的必要属性:用于 JDBC 的 DataSource。
在 MyBatis-Spring中,可使用SqlSessionFactoryBean来创建 SqlSessionFactory。
要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 绑定mybatis配置文件
一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径。
-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- mapperLocations属性接受多个资源位置。
这个属性可以用来指定 MyBatis 的映射器 XML 配置文件的位置。
属性的值是一个 Ant 风格的字符串,可以指定加载一个目录中的所有文件,或者从一个目录开始递归搜索所有目录。
-->
<property name="mapperLocations" value="classpath:com/hecn/mapper/*.xml"/>
</bean>
- sqlSessionTemplate:
SqlSessionTemplate
是 MyBatis-Spring 的核心。作为SqlSession
的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的SqlSession
。SqlSessionTemplate
是线程安全的,可以被多个 DAO 或映射器所共享使用。
<!--3.SqlSessionTemplate:就是我们使用的SqlSession
可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。-->
<bean id="session" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造方法注入sqlsessionFactory,因为它没有set方法 -->
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
<!--和上面等价<constructor-arg index="0" ref="sqlSessionFactory"/>-->
</bean>
- 给接口加实现类
package com.hecn.mapper;
import com.hecn.pojo.User;
import lombok.Setter;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
@Setter
public class UserMapperImpl implements UserMapper {
/**
* 在原来,我们所有的操作都使用sqlsession来执行;
*
* <p>现在,都使用sqlsessionTemplate;
*/
private SqlSession sqlSession;
/** @return 查询用户 */
public List<User> selectUser() {
// UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// return mapper.selectUser();
return sqlSession.getMapper(UserMapper.class).selectUser();
}
}
- 将自己写的实现类注入到spring容器中
<bean id="userMapper" class="com.hecn.mapper.UserMapperImpl">
<property name="sqlSession" ref="session"/>
</bean>
- 测试
13.Spring 声明式事务
13.1、事务
- 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
- 事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。
事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
事务四个属性ACID
- 原子性(atomicity)
- 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
- 一致性(consistency)
- 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
- 隔离性(isolation)
- 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
- 持久性(durability)
- 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中
13.2、spring中的事务管理
- 声明式事务:(交由容器管理)aop
- 编程式事务:需要在代码中编写,必须在每个事务操作业务逻辑中包含额外的事务管理代码
事务管理器:
<!-- jdbc事务管理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--<property name="dataSource" ref="dataSource"></property>-->
<constructor-arg ref="dataSource"/>
</bean>
事务通知:
<!--配置事务通知-->
<tx:advice id="interceptor" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
配置aop织入:
<!--配置aop-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.hecn.mapper.*.*(..))"/>
<aop:advisor advice-ref="interceptor" pointcut-ref="txPointcut"/>
</aop:config>