文章目录
文章内容为某站遇见狂神说视频学习内容
一.Spring简介
1.简介
spring框架是为了简化企业级开发
2.优点
- 开源的免费的框架(容器)
- 轻量级的,非入侵式的框架
- 控制反转(IOC) 面向切面编程(AOP)
- 支持事务的处理,对框架整合的支持
3.组成
4拓展
springBoot和springCloud
5.IOC理论
以前例子
UserDao
public interface UserDao {
void getUser();
}
UserDaoImpl
public class UserDaoImpl implements UserDao{
public void getUser(){
System.out.println("huoqushuju");
}
}
UserService
public interface UserServices {
public void getUser();
}
UserServiceImpl
public class UserServicesImpl implements UserServices{
private UserDao userdao=new UserDaoImpl();
public void getUser(){
userdao.getUser();
}
Mytest
public static void main(String[] args){
//用户实际调用的是业务层,不需要实际接触dao层
UserServices userservices=new UserServicesImpl();
userservices.getUser();
}
如果增加实现类,则需要修改业务层的代码,在真实项目中的代码量庞大,修改一次的代价十分昂贵。
可以使用一个Set接口实现,在UserServiceImpl中,修改代码为
private UserDao userdao;
//利用set进行动态实现值得注入
public void setUserdao(UserDao userdao) {
this.userdao = userdao;
}
public void getUser(){
userdao.getUser();
}
在Mytest中修改代码为
public static void main(String[] args){
//用户实际调用的是业务层,不需要实际接触dao层
UserServices userservices=new UserServicesImpl();
((UserServicesImpl) userservices).setUserdao(new MysqlDaoImpl());
userservices.getUser();
}
之前,程序是主动创建对象,控制权在程序员手里
使用Set注入后,程序不再具有主动性,而是变成被动的接受对象,程序员不再需要管理对象的创建,系统耦合性降低,可以专注业务的实现,这是IOC的原型!
IOC本质:
控制反转IOC(inversion of control),依赖注入DI(Dependency Injection) 是实现控制反转的一种方式。
(1)控制反转,没有IOC的程序中,我们使用面向对象编程,对象的创建与对象之间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,而使用控制反转之后,将对象的创建转移给第三方,也就是IOC容器,也就是说获得依赖对象的方式反转了。这样,应用程序只需要直接使用已经创建好并且配置好的组件。为了能让组件在IoC容器中被“装配”出来,需要某种“注入”机制。
(2)依赖注入,它解决了一个最主要的问题:将组件的创建+配置与组件的使用相分离,并且,由IoC容器负责管理组件的生命周期。
可以采用两种方式(1)xml文件进行配置(2)采用注解的方式
6.代码示例hellospring
Hello类、
package com.xu.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 + '\'' +
'}';
}
}
(1)基于xml配置元数据applicationContext.xml
因为IoC容器要负责实例化所有的组件,因此,有必要告诉容器如何创建组件,以及各组件的依赖关系。一种最简单的配置是通过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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring中这些都称为bean
bean=对象 就是new了一个hello()
原来 类型 变量名=new 类型()
Hello hello=new Hello()
现在 bean中的id就是变量名 class就是new的对象
property就是给对象中的属性设置值
这个过程就是控制反转-->
<bean id="hello" class="com.xu.pojo.Hello">
<property name="str" value="String">
</property></bean>
</beans>
(2)实例化容器
在mytest类中:
//获取Spring的上下文对象,Spring容器就是ApplicationContext,它是一个接口,有很多实现类,这里我们选择ClassPathXmlApplicationContext,表示它会自动从classpath中查找指定的XML配置文件。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
(3)使用容器
//我们的对象都在Spring容器中管理了,我们要使用,直接从里面去出来就可以啦1
Hello hello=(Hello) context.getBean("hello");
System.out.println(hello.toString());
7.总结
是实现不同的操作,只需要在xml配置文件中进行修改,也就是说
IOC:对象由spring创建,管理,装配!
二.IOC创建对象的方式
1.使用无参构造创建对象(默认)
2.假设使用有参构造函数创建对象
(1)参数下标赋值
<bean id="user" class="com.xu.pojo.User">
<constructor-arg index="0" value="xuan"></constructor-arg>
</bean>
(2)参数类型赋值(如果有多个变量类型相同,则会按照顺序进行赋值?)
<bean id="user" class="com.xu.pojo.User">
<constructor-arg type="java.lang.String" value="xuan"></constructor-arg>
</bean>
(3)直接通过参数名
<bean id="user" class="com.xu.pojo.User">
<constructor-arg name="name" value="xuun"></constructor-arg>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了,即在 ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext.xml”);时对象已经被创建
3.Bean的配置
bean就是java对象,由Spring创建和管理
id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
如果配置id,又配置了name,那么name是别名
name可以设置多个别名,可以用逗号,分号,空格隔开
如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
class是bean的全限定名=包名+类名
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
三.Spring配置
1.别名alias<alias></alias>
2.Bean的配置
id:bean的唯一标识符,相当于对象名
class:对象所对应的全限定名:包名+类型
name:也是别名,name可以同时取多个别名name=“user1 user2,user3;user4”
3.import(一般用于团队开发使用,可以将多个配置文件,导入合并成为一个)
四.依赖注入
(1)依赖:bean对象的创建依赖于容器
(2)注入:bean对象中的所有属性,由容器来注入
搭建环境,即指Bean对象所依赖的资源 , 由容器来设置和装配 .
1.构造器注入
参考本章第一节 IOC创建对象的方式
2.set方式注入(重要)
(1)复杂环境:
//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;
//address类
private String address;
(2)applicationContext.xml
<bean id="address" class="com.xu.pojo.Address"></bean>
<bean id="student" class="com.xu.pojo.Student">
<!--第一种,普通值注入,value-->
<property name="name" value="hh"></property>
<!--第二种,bean注入,ref-->
<property name="address" ref="address"></property>
<!--数组注入 -->
<property name="books" >
<array>
<value>红楼梦</value>
<value>三国演义</value>
</array>
</property>
<!--list注入 -->
<property name="hobbies">
<list>
<value>reading</value>
<value>game</value>
</list>
</property>
<!--map注入 -->
<property name="card">
<map>
<entry key="idcard" value="121212"/>
<entry key="cre" value="24234"/>
</map>
</property>
<!--set注入 -->
<property name="games">
<set>
<value>wangzherongy</value>
<value>hepingjingy</value>
</set>
</property>
<!--null注入 -->
<property name="wife">
<null/>
</property>
<!--properties注入 -->
<property name="info">
<props>
<prop key="学号">67167</prop>
<prop key="sex">男</prop>
</props>
</property>
</bean>
(3)测试类Mytest
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
Student student =(Student) context.getBean("student");
System.out.println(student.toString());
}
3.拓展方式注入
(1)p命名空间:
xmlns:p="http://www.springframework.org/schema/p"
可以直接注入属性的值:property
<!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
<bean id="student" class="com.xu.pojo.Student" p:name="狂神" p:age="18"/>
(2)c命名空间:
xmlns:c="http://www.springframework.org/schema/c"
,通过构造器注入:construct
4.Bean的作用域
(1)单例模式(Spring默认的机制)
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。
<bean id='' class='' scope="singleton">
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
测试 返回true,即同一对象
@Test
public void test03(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user==user2);
}
(2)原型模式
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
<bean id='' '' scope="prototype">
(3)其余的在web开发中应用
五.Bean的自动装配
1.自动装配说明
- 自动装配是Spring满足Bean依赖的一种方式
- Spring会在上下文中自动寻找,并自动给bean装配
- 在Spring有三种自动装配方式
(1)xml显式装配
(2)java中显式装配
(3)隐式自动装配
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
(1)组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
(2)自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;
组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。
2.Autowire Byname按名称自动装配
(1)会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid
(2)需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
<bean id="people" class="com.xu.pojo.people" autowire="byName">
当一个bean节点带有 autowire byName的属性时。
(1)将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
(2)去spring容器中寻找是否有此字符串名称id的对象。
(3)如果有,就取出注入;如果没有,就报空指针异常。
3.Autowire ByType自动装配
(1)会自动在容器上下文中查找,和自己对象属性类型相同的bean
(2)需要保证所有bean的class唯一
<bean id="people" class="com.xu.pojo.people" autowire="byType">
4.使用注解实现自动装配
(1)导入约束
(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"
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>
(3)使用注解@Autowired注入Bean
(1)@Autowired是按类型自动转配的,不支持id匹配。
(2)需要导入 spring-aop的包!
(3)@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。
(4)@Qualifier(value=“”)
(1)@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
(2)@Qualifier不能单独使用。
例子:SmsService 接口有两个实现类: SmsServiceImpl1和 SmsServiceImpl2,且它们都已经被 Spring 容器所管理。
// 报错,byName 和 byType 都无法匹配到 bean @Autowired private SmsService smsService; // 正确注入 SmsServiceImpl1 对象对应的 bean @Autowired private SmsService smsServiceImpl1; // 正确注入 SmsServiceImpl1 对象对应的 bean // smsServiceImpl1 就是我们上面所说的名称 @Autowired @Qualifier(value = "smsServiceImpl1") private SmsService smsService;
(5)@Resource
(1)@Resource如有指定的name属性,先按该属性进行byName方式查找装配;
(2)其次再进行默认的byName方式进行装配;
(3)如果以上都不成功,则按byType的方式自动装配。
(4)都不成功,则报异常。
(5)如果找到多个或者0个,报错
(6)@Autowired与@Resource异同
1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
3.@Autowired当按类型装配时,如果该类型的bean不止一个时(例如子类和父类都是父类一类,接口和接口实现类都是一类),会直接报错。1.可以使用@Primary注解指定被装配的bean。2.使用@Qualifier(value=)指定名称,按类型和名称装配。
3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。
- 既没指定name,也没指定type,自动按照名称装配(当注解写在字段上时,默认取字段名,当注解写在setter方法上时,默认取属性名进行装配。);如果没有匹配,则退而按照类型装配,找不到则抛出异常。
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
- 如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
(7)将一个类声明为 Bean 的注解有哪些?
@Component :通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。
@Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
@Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
@Controller : 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。
(8)@Component 和 @Bean 的区别是什么?
@Component 注解作用于类,而@Bean注解作用于方法。
@Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。
@Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。
六.Spring注解开发
1.引入spring-aop依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
2.配置扫描哪些包下的注解
<!--指定注解扫描包-->
<context:component-scan base-package="com.kuang.pojo"/>
3.bean注入
//@Component相当于<bean id="user" class="com.xu.pojo.User"></bean>
@Component("user")
public class User {
public String name="hh";
}
@Component相当于<bean id="user" class="com.xu.pojo.User">
4.使用注解注入属性
@Component("user")
public class User {
@Value("xu")
// 相当于配置文件中 <property name="name" value="xu"/>
public String name;
}
或者放在set函数上面
@Component
public class User {
public String name;
@Value("hhjj")
public void setName(String name) {
this.name = name;
}
}
@Value相当于<property name="name" value="xxjj">
5.衍生注解
@Component有几个衍生注解,在web开发中,按照mvc三层架构进行分层
- dao【@Repository】
- service【@Service】
- controller【@Controller】
这四个注解功能一样,都是将某个类注册到spring中,装配bean
6.自动装配置
见上面笔记
7.作用域
@Scope(“singleton”)单例模式
@Scope(“Prototype”) 原型模式
8.XML与注解比较
(1)XML可以适用任何场景 ,结构清晰,维护方便
(2)注解不是自己提供的类使用不了,开发简单方便
xml与注解整合开发 :推荐最佳实践
(1)xml管理Bean
(2)注解完成属性注入
(3)使用过程中, 可以不用扫描,扫描是为了类上的注解
七.完全使用java方式配置Spring
完全不使用xml进行配置,使用java语言进行配置
User
@Component
public class User {
@Value("hh")
private String name;
public String getName() {
return name;
}
}
config
//@Configuration代表这是一个配置类,相当于之前的applicationContext.xml
//也会由Spring容器托管,注册到容器中,因为本身也是一个@Component
@Configuration
public class config {
//注册一个Bean,相当于之前的bean标签
//这个方法的名字就是相当于bean中的id属性
//这个方法的返回值,相当于bean标签中的class属性
@Bean
public User user(){
return new User();//返回要注入到bean中的对象
}
}
Mytest
public class Mytest {
public static void main(String[] args) {
ApplicationContext context =new AnnotationConfigApplicationContext(config.class);
User getuser=(User) context.getBean("user");
System.out.println(getuser.getName());
}
}
八.AOP面向切面编程
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
1.代理模式
(springAOP和SpringMVC面试重点!)
代理模式的分类:
- 静态代理
- 动态代理
(1)静态代理
**角色分析**

- 抽象角色:一般使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
- 客户:访问代理对象的人
- 用户只关心接口功能,而不在乎谁提供了功能。上图中接口是 Subject。
- 接口真正实现者是上图的 RealSubject,但是它不与用户直接接触,而是通过代理。
- 代理就是上图中的 Proxy,由于它实现了 Subject 接口,所以它能够直接与用户接触。
- 用户调用 Proxy 的时候,Proxy 内部调用了 RealSubject。所以,Proxy 是中介者,它可以增强 RealSubject 操作。
代码示例
service
public interface service {
public void add();
public void delete();
public void search();
public void update();
}
serviceImpl
public class serviceImpl implements service{
//service service =new serviceImpl();
public void add() {
System.out.println("add");
}
public void delete() {
System.out.println("delete");
}
public void search() {
System.out.println("serach");
}
public void update() {
System.out.println("update");
}
}
proxy
因为设计原则是在尽量不改变源代码的情况下进行修改
//创建对象
private serviceImpl serviceimpl;
public void setServiceimpl(serviceImpl serviceimpl) {
this.serviceimpl = serviceimpl;
}
public void add() {
log("add");
serviceimpl.add();
}
@Override
public void delete() {
log("delete");
serviceimpl.delete();
}
@Override
public void search() {
log("search");
serviceimpl.search();
}
@Override
public void update() {
log("update");
serviceimpl.update();
}
public void log(String msg){
System.out.println("[debug]正在执行"+msg+"操作");
}
}
client
public class client {
public static void main(String[] args) {
//创建被代理对象
serviceImpl service=new serviceImpl();
//创建代理
proxy proxy=new proxy();
//代理对象
proxy.setServiceimpl(service);
proxy.delete();
}
}
(2)静态代理模式的优缺点:
优点:
可以让真实角色操作更加纯粹 不用去关注一些公共的业务
公共页就交给代理角色!实现了业务的分工
公共业务发生拓展的时候,方便集中管理
缺点
一个真实角色就会产生一个代理角色;代码量会翻倍~开发效率会变低
(3)动态代理
(1)动态代理的角色和静态代理的一样 .
(2)动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
(3)动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
基于接口的动态代理----JDK动态代理
基于类的动态代理–cglib
现在用的比较多的是 javasist 来生成动态代理
(4)动态代理的根据实现方式的不同可以分为 JDK 动态代理和 CGlib 动态代理。
JDK 动态代理:利用反射机制生成一个实现代理接口的类,在调用具体方法前调用InvokeHandler来处理。
CGlib 动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类
需要了解两个类:Proxy:生成代理类,InvocationHandler:调用处理程序,并返回结果
代码示例
Rent
public interface Rent {
public void rent();
}
LandLord
public class Landlord implements Rent {
public void rent() {
System.out.println("房东要出租房子");
}
}
ProxyInnovationHandler
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
//生成得到代理类
public Object newProxyInstance(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
@Override
//处理代理实例,并返回结果
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//动态代理的实质就是用反射机制来实现
Object result= method.invoke(target,objects);
return null;
}
Client
public static void main(String[] args) {
//真实角色
Landlord landlord=new Landlord();
//代理角色,目前还没有
ProxyInvocationHandler pih=new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象!
pih.setTarget(landlord);//设置要代理的对象
//动态生成代理类
Rent proxy=(Rent) pih.newProxyInstance();
proxy.rent();
}
优点
可以让真实角色操作更加纯粹 不用去关注一些公共的业务
公共页就交给代理角色!实现了业务的分工
公共业务发生拓展的时候,方便集中管理
动态代理的是一个接口,一般对应的都是一类业务
一个动态代理类可以代理多个类,只要是实现了一个接口即可
(4)动态代理的两种方式
Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:
2.AOP(Aspect Oriented Programming)
1.概念
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹
2.在spring中的作用
- 提供声明式事务
- 允许用户自定义切面
3.名词
(1)横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
(2)切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
(3)通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
(4)目标(Target):被通知对象。
(5)代理(Proxy):向目标对象应用通知之后创建的对象。
(6)切入点(PointCut):切面通知 执行的 “地点”的定义。
(7)连接点(JointPoint):与切入点匹配的执行点。
4.横切逻辑
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
5.在Spring中实现AOP
首先导入依赖包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
方式一:使用Spring的API接口
<!-- 注册bean-->
<bean id="userservice" class="com.xu.servicec.userServiceImpl"></bean>
<bean id="log" class="com.xu.log.log"></bean>
<bean id="afterLog" class="com.xu.log.AfterLog"></bean>
<!-- 使用Spring API接口-->
<!-- 配置AOP-->
<aop:config>
<!-- 切入点:expression表达式 execution(要执行的位置!* * * * *)-->
<aop:pointcut id="pointcut" expression="execution(* com.xu.servicec.userServiceImpl.*(..))"/>
<!-- 执行环绕增强-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
</beans>
方拾二使用自定义类实现AOP(主要是切面定义)
<bean id="diy" class="com.xu.diy.diyLog"></bean>
<!-- 自定义切面 ref要引用的类-->
<aop:config>
<aop:aspect ref="diy">
<!-- 切入点-->
<aop:pointcut id="diypointcut" expression="execution(* com.xu.servicec.userServiceImpl.*(..))"/>
<!-- 通知-->
<aop:before method="before" pointcut-ref="diypointcut"></aop:before>
<aop:after method="after" pointcut-ref="diypointcut"></aop:after>
</aop:aspect>
</aop:config>
</beans>
自定义
public class diyLog {
public void before(){
System.out.println("======qian");
}
public void after(){
System.out.println("======hou");
}
}
方法三:使用注解的方式
<bean id="annotationPoint" class="com.xu.diy.annotationPointCut"></bean>
<aop:aspectj-autoproxy/>
@Aspect//标注这个类是切面
public class annotationPointCut {
@Before("execution(* com.xu.servicec.userServiceImpl.*(..))")
public void before(){
System.out.println("before");
}
@After("execution(* com.xu.servicec.userServiceImpl.*(..))")
public void after(){
System.out.println("after");
}
@Around("execution(* com.xu.servicec.userServiceImpl.*(..))")
public void around(ProceedingJoinPoint pj) throws Throwable {
System.out.println("hrbefore");
Object proceed = pj.proceed();
System.out.println("hhafter");
}
}
九.spring中的事务管理
1.事务
(1)事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
(2)事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。
事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
2.事务四个属性ACID
(1)原子性(atomicity)
事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
(2)一致性(consistency)
一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
(3)隔离性(isolation)
可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
(4)持久性(durability)
事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中
3.声明式事务管理
(1)一般情况下比编程式事务好用。
(2)将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
(3)将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。
(1)头文件导入tx
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
(2)JDBC事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
(3)配置好事务管理器后我们需要去配置事务的通知
<!--配置事务通知-->
<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="search*" propagation="REQUIRED"/>
<tx:method name="get" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
(4)spring事务传播特性
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
(5)配置AOP
导入aop的头文件!
<!--配置aop织入事务-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
十.Springmvc
1.核心组件
DispatcherServlet :核心的中央处理器,负责接收请求、分发,并给予客户端响应。
HandlerMapping :处理器映射器,根据 uri 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。
HandlerAdapter :处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler;
Handler :请求处理器,处理实际请求的处理器。
ViewResolver :视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端
2.工作流程
- 客户端(浏览器)发送请求, DispatcherServlet拦截请求。
- DispatcherServlet 根据请求信息调用 HandlerMapping 。HandlerMapping 根据 uri 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。
- DispatcherServlet 调用 HandlerAdapter适配执行 Handler 。
- Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServlet,ModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View。
- ViewResolver 会根据逻辑 View 查找实际的 View。
DispaterServlet 把返回的 Model 传给 View(视图渲染)。
把 View 返回给请求者(浏览器)