前言
用来做笔记而已。试试博客啥样;
一、什么是spring
一种轻量级的java开发框架。核心是控制反转(ioc)和面向切面编程(aop);
主要作用是为了解耦和,降低代码之间的耦合度。模块之间的关系通过配置文件进行说明。
优点:轻量,通过ioc降低对象之间的依赖关系,由容器管理对象;面向切面编程,从繁杂的事务当中解脱。
二、控制反转(ioc)
控制反转是一种思想。将传统意义上由程序员直接操纵的对象的调用权交给容器,由容器进行对象的创建,属性赋值和管理。
控制:创建对象,完成对象的属性赋值和依赖管理
反转:将开发人员直接管理对象的权利交给代码之外的容器实现
传统正转:通过new 的方式进行对象的创建,主动管理对象
public void main(String[] args) { MyTest test = new MyTest(); }
javaweb中的ioc体现:创建类继承HttpServlet的时候,在xml文件中完成servlet的注册。通过<servlet-name>寻找<servlet-class>进行创建,这里使用ioc的思想,将servlet对象的创建交付给容器tomcat进行创建。
1.第一个spring程序
- 添加spring依赖:spring-context
- 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">
<bean id="Test1" class="com.righteye.impl.SomeServiceImpl" scope="prototype"></bean>
<bean id="Test2" class="com.righteye.impl.otherServiceImpl"></bean>
<!--创建非自定义对象-->
<bean id="mydate" class="java.util.Date"></bean>
</beans>
spring配置文件,每一个<bean>代表一个对象,默认为单例模式;属性值id表示需要创建的对象对应的类(不能是接口),scope表示创建的对象是单例还是多例;prototype表示多例
- 编写测试用例(这里使用的maven创建的项目)
@Test
public void Test02() {
String config = "applicationContext.xml";
// ClassPathXmlApplicationContext表示从类路径中加载配置文件
// 类路径表示从target/class下面寻找文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
// 通过getBean创建一个对象
SomeService s = (SomeService)ac.getBean("Test1");
s.doSome();
OtherService other = (OtherService) ac.getBean("Test2");
other.doOther();
}
ApplicationContext是一个接口,用于加载spring的配置文件;其实现类ClassPathXmlApplicationContext,用于处理配置文件存放在项目的类路径下。
ApplicationContext容器,在容器初始化的时候,一次性创建所有<bean>标签声明的对象,调用无参构造器;占用内存,以后可以直接通过getBean获取对象
2.基于xml的di
di:依赖注入,当程序在运行过程中,如果需要调用另一个对象进行协作,不需要在代码中创建被调用者,依赖于外部容器,由外部容器创建传递给程序
实现对象创建后的属性赋值。分为set注入和构造注入
1)使用set()注入
- .set注入:也成为设值注入,进行赋值的类需要由set()方法,通过set方法由容器完成参数的赋值;传递的参数可以是简单类型或引用类型
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="student" class="com.righteye.ba01.Student">
<property name="name" value="张三"></property>
<property name="age" value="18"></property>
<property name="school" ref="MySchool"></property>
</bean>
<bean id="MySchool" class="com.righteye.ba01.School">
<property name="name" value="北京大学"></property>
<property name="address" value="北京"></property>
</bean>
</beans>
<property>标签,为对象进行赋值。属性name表示类中的属性名;value表示类的属性值,由容器完成注入;
简单类型(基本数据类型+String)将value的值通过相应的set方法完成注入
引用类型:如school, 赋值的使用不使用value, 使用ref;
语法<bean id="xxx", ref="yyy的id">
Student.java
package com.righteye.ba01;
public class Student {
private String name;
private int age;
private School school;
public Student() {
}
public void setName(String name) {
System.out.println("setName方法被调用");
this.name = name;
}
public void setAge(int age) {
System.out.println("setAge方法被调用");
this.age = age;
}
public void setSchool(School school) {
this.school = school;
}
}
- 构造注入(理解):有参构造函数的属性注入
<bean id="MyStudent1" class="com.righteye.ba02.Student">
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="name" value="zs"></constructor-arg>
</bean>
<bean> 子标签: <constructor-arg> 构造函数参数
2)引用类型的自动注入
对于引用类型,可以不再配置文件中显示的注入。通过autowire属性隐式完成,分为两种:byName和byType
在进行属性赋值的时候,也可以直接使用内部bean,类似匿名内部类,该对象仅对调用者可见
<!--使用内部bean-->
<bean id="user5" class="com.righteye.spring.day01.User">
<property name="username" value="乔巴"></property>
<property name="age" value="18"></property>
<property name="school">
<bean class="com.righteye.spring.day01.School">
<property name="name" value="第五幼儿园ben"></property>
<property name="address" value="nova4游戏商城"></property>
</bean>
</property>
</bean>
- byName:按名称自动注入
- 配置文件中被调用者的<bean> id值与代码中调用者的属性名相同;由容器自动完成属性注入
<?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.righteye.ba01.Student" autowire="byName">
<property name="name" value="张三"></property>
<property name="age" value="18"></property>
</bean>
<bean id="school" class="com.righteye.ba01.School">
<property name="name" value="北京大学"></property>
<property name="address" value="北京"></property>
</bean>
</beans>
首先在调用者添加autowire属性,设置为byName, 调用者的School属性名为school,被调用者的id为school,属性名称相同,可以完成属性的自动注入。
- byType:按类自动注入
- 配置文件中被调用者bean的class属性指定的类与调用者bean的某引用属性类型同源
- 同源:相同类;具有继承关系;接口和实现类的关系
- 同源关系只能同时存在一种
<bean id="MyStudent" class="com.righteye.ba03.Student" autowire="byType">
<property name="age" value="18"></property>
<property name="name" value="王五"></property>
</bean>
<!--byType的自动注入:相同类-->
<!--<bean id="MySchool" class="com.righteye.ba03.School">-->
<!--<property name="name" value="北京大学"></property>-->
<!--<property name="address" value="北京"></property>-->
<!--</bean>-->
<!--byType的自动注入:具有继承关系-->
<bean id="MyPrimarySchool" class="com.righteye.ba03.PrimarySchool">
<property name="name" value="人民大学"></property>
<property name="address" value="北京"></property>
</bean>
3) <value>属性标签
在为属性进行赋值的时候,除了使用property中的value属性,也可以使用<value>标签 + 字面量的形式进行赋值;
在进行属性赋值的时候,可能会有特殊字符的存在,可以使用关键字<![CDATA[]]>
<!--当属性值中有特殊字符的时候可以添加关键字-->
<!--属性赋值可以使用<value></value>标签-->
<bean id="user2" class="com.righteye.spring.day01.User">
<property name="username">
<value><![CDATA[<<王五>>]]></value>
</property>
<property name="age" value="18"></property>
</bean>
3.使用p标签
p标签的使用可以加快开发效率,用来代替<property />
<!--使用p标签,简化配置文件-->
<bean id="user" class="com.righteye.spring.day01.User"
p:username="ww"
p:age="20"></bean>
<bean id="teacher" class="com.righteye.spring.day01.Teacher"
p:name="娜美" p:age="18"
p:uList-ref="user"
p:uSet-ref="user">
</bean>
在IDEA使用p标签后,会在配置文件上面自动导入命名空间:
xmlns:p="http://www.springframework.org/schema/p"
4.基于注解的di
使用注解,不需要在spring的配置文件中声明bean实例。需要在spring配置文件中声明组件扫描器,对指定的基本包扫描注解
<--扫描base-package路径下的所有类-->
<context:component-scan base-package="xx包名"/>
常用注解:
-
Component:用于创建不清楚具体功能的对象;添加在需要创建对象类的上面
-
@Component(value=id) : 这里的id相当于<bean>中的id,具有唯一性
-
当Component中不指定value的时候,默认id为类名首字母小写
-
-
Respority:创建持久层对象,是对@Component的细化
-
Service:创建业务逻辑层对象
-
Controller:创建控制层对象
-
Value:简单类型属性的注入,在需要赋值的属性上面添加@Value(属性值);完成自动注入,属性不需要set方法
@Value(value="张三")
private String name;
-
Autowired:默认byType的自动注入(对于引用类型);在引用类型属性上面添加@Autowired注解;含有一个required属性,默认为true, 当属性匹配错误的时候系统运行停止;为false,表示忽略,没有赋值的内容为null
-
Quality:修改引用类型属性自动注入的方式为byName, @Quality(value="id")
// 用于完成自动注入,默认byType
@Autowired(required = false)
@Qualifier("mySchool")
private School school;
- Resource:由JDK提供的注解,也是为了实现自动注入,当不提供参数的时候,默认方式为byName,如果没有匹配成功,采用类型匹配
// @Resource注解若不带任何参数,采用默认按名称的方式注入
// 按名称不能注入bean,则会按照类型进行Bean的匹配注入。
@Resource
private School school;
//@Component 未指明id,默认为类名首字母小写 school == school, 匹配成功
// id = MySchool != school, 所以再看类型是否匹配
@Component("MySchool")
public class School {
@Value("清华大学")
private String name;
@Value("北京")
private String address;
}
5.补充内容
1).<bean>的继承
<bean>中包含一个属性parent, 可以继承其他 bean对象的属性;可以定义一个统一的bean的作为模板,由其他bean继承属性,相同的属性进行赋值后者会覆盖前者
<!--定义了User对象的模板-->
<bean id="user" class="com.righteye.spring.day01.User"
p:age="18" p:username="ww"
abstract="true"></bean>
<bean id="user_son" parent="user"></bean>
<bean id="teacher" class="com.righteye.spring.day01.Teacher"
p:name="索隆"
p:age="19" autowire="byType"></bean>
其中:parent = “对象id”, 用来继承id; 在模板bean中可以添加abstract, 表示这个对象不会被创建,只能由其他bean继承
2)使用${}从配置文件读取数据
在xml文件中添加 <context:property-placeholder>, 参数location的值为配置文件路径位置
这里以数据库配置文件为例:
<context:property-placeholder location="classpath:demo06/db.properties"></context:property-placeholder>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${db.driver}"></property>
<property name="url" value="${db.url}"></property>
<property name="username" value="${db.user}"></property>
<property name="password" value="${db.password}"></property>
</bean>
3)bean对象的生命周期
<bean>标签中还包括init-method和destroy-method属性,属性值为需要执行的函数名;用来提供对象创建后的初始化操作和销毁前进行的操作(如释放内存)
<bean id="user" class="com.righteye.spring.day01.User"
p:age="18" p:username="索隆"
init-method="init"
destroy-method="close"></bean>
4)后置处理器
后置处理器允许在bean初始化前后对bean对象进行额外的操作
后置处理器会对所有容器对象都会执行相应的操作
后置处理器需要一个实现了BeanPostProcessor接口的java类,然后实现相应的方法去处理特定的时间点
public class PostHandler implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeforeInitialization:" + bean + " " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeforeInitialization:" + bean + " " + beanName);
return bean;
}
}
在spring配置文件中创建后置处理器对象
<bean class="com.righteye.spring.day01.handler.PostHandler"></bean>
5)使用静态工厂创建bean对象
public class StaticUserFactory {
private static User user;
public static User getUser(Integer age, String username) {
user = new User();
user.setAge(age);
user.setUsername(username);
return user;
}
}
在bean对象中导入静态工厂类以及使用factory-method调用的方法, 通过构造器对属性进行赋值
<bean id="static_user" class="com.righteye.spring.day01.factory.StaticUserFactory"
factory-method="getUser">
<constructor-arg name="username" value="山治"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
</bean>
三、面向切面编程(AOP)
动态代理:通过动态生成代理对象,有代理对象创建目标类;并对目标类的业务功能进行增强
jdk的动态代理:是通过接口完成动态代理,主要类为InnovationHandler, Method, Proxy进行操纵
cglib的动态代理:不需要接口,通过继承的方式对目标类进行增强
动态代理的优势:
使目标类专心业务逻辑;
提供代码复用率
解耦合,让业务功能和非业务功能分离
1)什么是aop?
面向切面编程;基于动态代理,是动态代理的一种规范化,把动态代理的实现步骤进行了统一(统一jdk动态代理和cglib代理情况下的差异),方面进行编码
2)aop的术语
Aspect: 切面, 给目标类增加的功能,就是切面。例如日志,事务都是切面
切面的特点:一般都是非业务方法,独立使用的
JoinPoint:连接点,连接业务方法和切面的位置。
Pointcut:切入点,指连接点方法的集合
目标对象:给哪个类添加切面
Advice:通知,通知切面的执行时间
3)aop实现
spring框架中提供了aop规范,主要使用在事务处理,但一般不使用,比较笨重;
aspectJ, 由ecplise基金会提供的,开源专门做aop框架。spring中继承了aspectJ框架,可以使用xml配置文件/使用注解(5个:Before, AfterReturing, Around, After, AfterThrowing)
使用aspectJ实现aop的基本步骤:
- 建立maven项目
- 添加依赖
- spring依赖
- aspectJ的依赖
- junit单元测试
- 创建目标类:接口和他的实现类
- 创建切面类
- 在类的上面使用@aspect
- 在类中定义方法,即切面要执行的功能代码;在方法上面加入aspectJ中的注解,指定切入点表达式
- 创建配置文件
- 声明目标类和切面类
- 声明aspectJ框架中的自动代理生成器标签,用来完成代理对象的创建。
<!--添加ajpectJ依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
- 切入点表达式:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
modifiers-pattern 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型 ?表示可选的部分
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
- 配置文件 applicationContext.xml
自动代理生成器原理:自动代理生成器会寻找所有添加@Aspect的类,然后再由切面类通过切面点找到需要增强方法的位置,并通过注解找到添加的时间
<!--创建目标类-->
<bean id="myTarget" class="com.righteye.ba02.SomeServiceImpl"></bean>
<!--创建切面类-->
<bean id="myAspect" class="com.righteye.ba02.MyAop"></bean>
<!--创建自动代理生成器-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 注解@Before
前置通知,包含一个JoinPoint参数,可以获取切入点表达式,方法签名,目标对象等
要求:方法public, 没有返回值,可以有参数
/**
* 前置通知:方法公有
* 没有返回值,方法名称自定义,参数可以有或没有
*/
@Before(value="execution(public * com.righteye.ba02.SomeServiceImpl.doSome(..))")
public void doLog() {
System.out.println("非业务方法:" + new Date());
}
- 注解@AfterReturning
/**
* @AfterReturning
* 后置通知
* 方法公有,没有返回值,有参数,方法名称自定义
*
* 注解参数:value :切入表达式
* returning: 用来接收目标方法的返回值,必须和通知方法的参数名相同
* 对于目标方法中获取的参数,如果是String, 因为字符串是不可变的所以修改结果没有改变
* 如果是引用类型,因为引用传递的原因可以修改
*/
@AfterReturning(value="execution(public * com.righteye.ba02.SomeServiceImpl.doOther(..))"
, returning="obj")
public void doThing(Object obj) {
System.out.println("后置通知:提交事务");
obj += "我被修改";
System.out.println("修改后的obj参数:" + obj);
}
- 注解@AfterReturning
/**
* @Around 环绕通知
* 方法公有, 有返回值,自定义方法名称,有参数:ProceddiingJoinPoint
*
* @Around 类似JDK的动态代理,而参数ProceedingJoinPoint类似Method,能够调用目标类方法
* 该注解常常使用在事务的提交方面;能够控制目标类方法是否能够正常被调用
*/
@Around(value="execution(* com.righteye.ba02.SomeServiceImpl.doAround(..))")
public void doAroundThing(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Around通知前置任务:" + new Date());
pjp.proceed(); // 控制目标方法是否被调用
System.out.println("Around通知后置任务:提交事务");
}
上述使用aop时候切入点表达式需要写在代码中方法的上面,耦合性过高
通过xml配置aop的切入点表达式:具体配置在这篇文章
https://blog.csdn.net/qq_45888932/article/details/122543155
四、事务
1.什么是事务?
事务是一组sql语句的集合,希望这一组sql语句的执行是一致的,要么同时发生,要么都不发生
2.在java代码中的dao层时,会调用一组sql语句操作数据库,所以在dao层的时候应该使用事务
由于可能使用多种不同的工具访问数据库,如mysql, mybatis, 其他数据库访问技术,造成在事务处理的时候方法不同,为了解决这个麻烦,spring统一,抽象出处理步骤,屏蔽了具体实现步骤
声明式事务:把事务相关的资源和内容提交给spring,由spring完成事务的处理
3.spring处理事务的步骤:
- 使用事务管理对象,是一个接口和他众多实现类的集合。代替完成事务的处理
接口:PlatformTransactionManger,定义了事务的commit和roolback
实现类:将访问数据的库的不同技术都做了相应的封装
在配置文件中使用<bean id="xxx" class="…..“/> 定义相应的访问数据库的方法
- 事务的类型
- 事务的隔离级别:
READ_UNCOMMITTED:读未提交,没解决任何并发问题
READ-COMMITTED:读已提交。解决脏读,存在不可重复读和幻读
REPEATABLE_READ: 可重复读。存在幻读问题
SERIALIZABLE:串行化
- 事务的超时时间:表示一个方法最长执行时间
- 事务的传播行为:控制业务方法是否有事务,是什么种类的事务
- PROPAGATION_REQUIRED
- PROPAGATION_REQUIRES_NEW
- PROPAGATION_SUPPORTS
- 事务提交的时机:
- 当业务方法没有抛出异常的时候,提交事务
- 当抛出运行时异常,回滚事务
-
如果是ERROR或者编译时异常,提交事务
- 事务处理的解决方案
- 适用于中小型项目,注解方案
使用@Transaction增加事务
1.在配置文件中声明事务管理器<bean …/>
2.开启spring事务注解驱动。spring使用aop机制,对添加@Transactional注解所在类创建代理方法,给方法添加事务功能;这里的注解驱动要选择类名后缀tx的
使用的是aop的环绕通知
<tx:annotation-driven transaction-manager="事务管理器id">
3.给方法添加@Transactional注解
在需要添加事务的业务方法上面添加注解:@Transactional
- 声明式事务处理
使用aspectJ提供的aop机制创建代理对象完成事务操作
第一种方法的缺点:使用XML配置事务代理的方式,每个目标类都需要配置事务代理。当目标类较多,配置文件会变得非常臃肿。 在每个方法的上面都要添加注解。
java代码和xml的配置文件没有完全分离
步骤:
1.添加aspectJ的依赖
2.配置事务管理器
3.配置事务通知。用于指定将事务以什么方式加入到哪些方法
4.配置增强器。使用aop机制,给目标类创建代理对象
声明式事务处理使用的aspectJ中的aop机制,所以有容器声明的最终对象式通过代理模式产生的对象
总结
用来做笔记而已。试试博客啥样;
刚开始学,很多漏洞,望更正