Spring
一、简介
Spring 是一个开源的 Java/Kotlin 应用框架,它旨在简化企业级应用程序的开发。Spring 框架为开发者提供了一种全面编程和配置模型,用于现代基于 Java 的应用程序。Spring 框架的核心特性是控制反转(IOC)和面向切面编程(AOP),这些特性使得开发更加灵活和易于管理。spring就是一个轻量级的控制反转和面向切面的编程的框架。
优点
- spring是一个轻量级、非入侵的框架。
- spring是一个开源免费的框架。
- 控制反转(IoC) 和面向切面编程( AOP) 使得应用程序的各个部分更加独立,降低了模块间的耦合度。
- 支持事务的处理,对框架整合的支持
二、IOC
简介
IOC即控制反转,是一种思想,是一种面向对象编程中的设计原则,主要用于解耦程序组件之间的依赖关系,DI(依赖注入)是实现IoC的一种方法。对象由spring创建、管理、装配
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特点对象的方式。在Spring中实现控制反转的是Ioc容器,其实现方法是依赖注入(DI)。其实控制反转的目的就是为了达到分成解耦的效果。
分层解耦
分成解耦:软件工程中一种重要的设计思想和方法论,它通过分层和降低层次之间的耦合度来提高系统的灵活性、可维护性和可扩展性。
原来我们的代码是这样的
service实现层
public class serviceImpl {
private DaoImpl d = new DaoImpl();
private void gettest(){
d.test();
}
}
Dao实现层
public class DaoImpl {
public void test(){
System.out.println("test方法被调用");
}
}
这样的代码需要在*service实现层创建对象,耦合度高,不利于管理,而且实际主动权在程序员,不具有多态性
控制反转后
service实现层
public class serviceImpl {
private Dao d;
public void setD(Dao d) {
this.d = d;
}
public void gettest(){
d.test();
}
}
Dao接口层
public interface Dao {
void test();
}
Dao实现层
public class DaoImpl implements Dao{
@Override
public void test(){
System.out.println("test方法被调用");
}
}
这样的就使得Dao实现层与service实现层进行了解耦,耦合度低,利于管理,实际主动权用户,具有多态性
依赖注入
依赖:Bean对象的创建依赖于容器
注入:Bean对象中的所有属性,由容器注入
(1)构造器注入
简介:基于构造函数的 DI 是通过容器调用带有许多参数的构造函数来完成的,每个参数代表一个依赖。
无参构造
Bean文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.test.User">
<property name="name" value="张三"/>
</bean>
</beans>
实体类
public class User {
private String name;
public User() {
System.out.println("User的无参构造被调用了");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
测试类
public class test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
}
}
当运行测试类后的结果
有参构造
(1)构造函数参数名
bean文件
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="name" value="张三"/>
</bean>
实体类
public class User {
private String name;
public User(String name) {
System.out.println("User的有参构造被调用了");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
(2)构造函数参数索引(不实用,想了解可以看访问文档)
(3)构造函数参数类型匹配(不实用,想了解可以看访问文档)
问题1:在加载文件后Bean就被创建了吗??
结论1:当Bean文件被加载时,Bean对象就都创建好了
(2)Set注入
简介:基于 Setter 的 DI 是通过容器在调用无参数的构造函数或无参数的 static
工厂方法来实例化你的 bean 之后调用 Setter 方法来实现的。
各数据类型的注入方法,如下
pojo类
@Data
public class Student {
private String name;
private User user; //这是个User类,里面有个name是个Sring类型
private String[] arr;
private List<String> list;
private Map<String,String> map;
private Set<String> set;
private String nu;
private Properties properties;
}
Bean文件
<bean id="user" class="com.qingshan.qingshan.Student">
<property name="name" value="name" />
<property name="user">
<bean class="com.qingshan.qingshan.User">
<property name="name" value="User_name" />
</bean>
</property>
<property name="arr">
<array>
<value>arr1</value>
<value>arr2</value>
<value>arr3</value>
</array>
</property>
<property name="nu">
<null/>
</property>
<property name="list">
<list>
<value>1</value>
<value>2</value>
</list>
</property>
<property name="map">
<map>
<entry key="an entry" value="just some string"/>
<entry key="a ref" value="myDataSource"/>
</map>
</property>
<property name="set">
<set>
<value>info</value>
<value>info1</value>
<value>info1</value>
</set>
</property>
<property name="properties">
<props>
<prop key="建1">值1</prop>
<prop key="建2">值2</prop>
</props>
</property>
</bean>
使用c命名空间的XML快捷方式
注:需要导入配置 xmlns:c=“http://www.springframework.org/schema/c”
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
<!-- 原本的写法 -->
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg name="thingTwo" ref="beanTwo"/>
<constructor-arg name="thingThree" ref="beanThree"/>
<constructor-arg name="email" value="something@somewhere.com"/>
</bean>
<!-- 使用c命名空间后 -->
<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>
</beans>
使用p命名空间的XML快捷方式
注:需要导入配置 xmlns:p=“http://www.springframework.org/schema/p”
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
<!-- 使用p名空间后 -->
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
</beans>
Bean的装配
在Spring中有三种装配的方式
(1)在xml中显示配置(以上用的都是这一种)
(2)在java中显示配置(重点、推荐)
Spring的Java配置支持的核心工件是 @Configuration
注解的类和 @Bean
注解的方法。
注入方法
@Configuration //加了这个注解相当于XML配置文件,底层也是IOC容器
@ComponentScan("com.test.pojo") //组件扫描
@Import(mytest.class) //加载其他的Configuration注册的类
public class AppConfig {
//装配一个Bean,相当于一个Bean标签
//方法名即是id名
//方法的返回值就相当于class属性
@Bean
public MyServiceImpl myService() {
return new MyServiceImpl();
}
}
获取容器的方法
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean("myService");
}
(3)隐式的自动装配
没自动装配的
<bean id="cat" class="com.qingshan.qingshan.Cat" />
<bean id="dog" class="com.qingshan.qingshan.Dog" />
<bean id="person" class="com.qingshan.qingshan.People">
<property name="cat" ref="cat" />
<property name="dog" ref="dog" />
</bean>
使用自动装配的
<bean id="cat" class="com.qingshan.qingshan.Cat" />
<bean id="dog" class="com.qingshan.qingshan.Dog" />
<!-- byuName:会自动在容器上下文中查找,和自己对象属性名相同的Beanid -->
<!-- byuType:会自动在容器上下文中查找,和自己对象属性类型相同的Bean -->
<bean id="person" class="com.qingshan.qingshan.People" autowire="byName" />
(4)注解实现自动装配
需要在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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
@Autowired自动装配
自动装配到属性上,可以免去set方法的代码
public class VisualController {
@Autowired
private VisualService v1;
@Autowired(required = false) //将required设置为false,方法的参数可以为空
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
@Resource自动装配
自动装配到属性上,可以免去set方法的代码,Resource是java自带的,脱离spring环境也可使用
public class SimpleMovieLister {
@Resource
private MovieFinder movieFinder;
@Resource(name="my") //可以设置name参数,表示找到Bean中id为my的装配
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
@Qualifiers辅助装配
可以更加精准的装配到Bean
public class MovieRecommender {
@Autowired
@Qualifier(value="name") //name为IOC容器中Bean的id名,
private MovieCatalog movieCatalog;
// ...
}
@Nullable
允许指定的属性为空
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
...
}
}
注:@Resource @Nullable@Antowired等这些注解能被识别到,但是@Component、@Controller、@Service等这些注解不能被识别到,此时就需要用到 <context:component-scan >
Bean的作用域
有6种模式
单例模式(singleton)(默认)
不管怎么创建这个Bean对象都是始终用的唯一的一个bean对象
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
原型模式(prototype)
每次创建的bean对象都是不同的
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
Bean的别名
(1)使用name取别名(推荐,可取多个)
将Bean名称为user的取多个个别名分别为为u1和u2,多个别名之间用逗号隔开,可以使用这个别名获取到Bean对象
<bean id="user" class="com.qingshan.qingshan.User" name="u1,u2">
<constructor-arg name="name" value="7500000"/>
</bean>
(2)使用alias取别名
将Bean名称为fromName的取一个别名为toName,可以使用这个别名获取到Bean对象
<alias name="fromName" alias="toName"/>
import 标签
一般用于团队开发,使用一个或多个 <import/>
元素的出现来从另一个或多个文件中加载Bean定义。
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
</beans>
三、使用注解开发
@Component
使用@Component将类注入Bean
使用@Compoent注解在类上相当于以下
<bean id="类名首字母小写" class="com.test" />
衍生注解
- @Controller (控制层)
- @Service (业务层)
- @Repository (数据访问层)
这些注解的作用是一样的,只是为了好区别各层
@Value
使用@value注解给属性赋值
public class Dog {
@Value("狗")
private String name;
void show(){
System.out.println("狗叫");
}
}
相当于xml的
<property name="name" value="狗" />
@value通常用于注入外部化properties。
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("${catalog.name}") String catalog) {
this.catalog = catalog;
}
}
catalog.name=MovieCatalog
@Scope
使用@Scope注解设置类的设计模式
//把类设为原型模式
@Scope("prototype"))
public class Test {
四、AOP
AOP,即面向切面编程,是一种编程范式,用于提高软件的可维护性、可扩展性和可重用性。它通过预编译方式和运行期间动态代理实现程序功能的统一维护,允许开发者在不修改源代码的情况下,为程序添加额外的功能。
通知类型
使用AOP需要导入依赖包需要导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.21</version>
</dependecy>
(1)spring实现AOP
前置通知
public class Log implements MethodBeforeAdvice {
/**
* @Description: 前置通知
* @Param: [method 要执行的目标对象方法, args 参数, target 目标对象]
* @return: void
* @Author: Lin
* @Date:
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"."+method.getName());
}
}
后置通知
public class Log2 implements AfterReturningAdvice {
/**
* @Description: 后置通知
* @Param: [returnValue 方法返回值, method 要执行的目标对象方法, args 参数, target 目标对象]
* @return: void
* @Author: Lin
* @Date:
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("Log2: " + method.getName() + " return " + returnValue);
}
}
Bean文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userimpl" class="com.xing.springaop.UserImpl" />
<bean id="log" class="com.xing.springaop.Log" />
<bean id="log2" class="com.xing.springaop.Log2" />
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.xing.springaop.UserImpl.*(..))" />
<!--执行环绕增加-->
<aop:advisor pointcut-ref="pointcut" advice-ref="log"/>
<aop:advisor pointcut-ref="pointcut" advice-ref="log2"/>
</aop:config>
</beans>
测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//动态代理的是接口
User user = (User) context.getBean("userimpl");
user.add();
}
}
(2) 自定义类实现AOP
dir类
public class Dir {
public void before(){
System.out.println("---------前置通知---------");
}
public void afterReturning(){
System.out.println("---------后置通知---------");
}
}
Bean文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userimpl" class="com.xing.springaop.UserImpl" />
<bean id="log" class="com.xing.springaop.Log" />
<bean id="log2" class="com.xing.springaop.Log2" />
<bean id="dir" class="com.xing.springaop.Dir" />
<aop:config>
<!--自定义的切面类:dir-->
<aop:aspect ref="dir">
<!--切入点-->
<aop:pointcut id="poin" expression="execution(* com.xing.springaop.UserImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="poin" />
<aop:after method="afterReturning" pointcut-ref="poin" />
</aop:aspect>
</aop:config>
</beans>
测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//动态代理的是接口
User user = (User) context.getBean("userimpl");
user.add();
}
}
(3) 注解实现
@Component //加载类为IOC容器
@Aspect //设置类为切面
public class PointCut {
//切入点
@Before("execution(* com.xing.springaop.UserImpl.*(..))")
public void before() {
System.out.println("前置通知");
}
//切入点
@After("execution(* com.xing.springaop.UserImpl.*(..))")
public void after(){
System.out.println("后置通知");
}
}
五、声明式事务
1、事务
- 将一组业务当成一次任务执行要么都成功,要么都失败
- 使数据保持一致性
事务的ACID原则:
- 原则性
- 一致性
- 隔离性
- 持久性
事务类型
开启 Spring 的事务处理功能需要在spring的xml文件中加入以下内容
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
配置事务
<!--设置事务通知-->
<tx:advice id="txAdvuce" transaction-manager="transactionManager">
<tx:attribute>
<tx:method name="*" propagation="REQUIRED"/> //表示在通知的事务中都开启REQUIRED事务
</tx:attribute>
</tx:advice>
<!-- 配置事务的切入 -->
<aop:config>
<aop:pointcut id="txPoinCut" expression="execution(* com.xing.springaop.*.*(..))" />
<aop:advisor advice-ref="txAdvuce" pointcut-ref="txPoinCut" />
</aop:config>