Spring开端
Spring项目的创建
首先,我们用Maven创建工程并引入spring-context
依赖:
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.8</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.6.RELEASE</version> </dependency>
|
我们先编写一个MailService
,用于在用户登录和注册成功后发送邮件通知:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class MailService { private ZoneId zoneId = ZoneId.systemDefault();
public void setZoneId(ZoneId zoneId) { this.zoneId = zoneId; }
public String getTime() { return ZonedDateTime.now(this.zoneId).format(DateTimeFormatter.ISO_ZONED_DATE_TIME); }
public void sendLoginMail(User user) { System.err.println(String.format("Hi, %s! You are logged in at %s", user.getName(), getTime())); }
public void sendRegistrationMail(User user) { System.err.println(String.format("Welcome, %s!", user.getName()));
} }
|
再编写一个UserService
,实现用户注册和登录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class UserService { private MailService mailService;
public void setMailService(MailService mailService) { this.mailService = mailService; }
private List<User> users = new ArrayList<>(List.of( new User(1, "bob@example.com", "password", "Bob"), new User(2, "alice@example.com", "password", "Alice"), new User(3, "tom@example.com", "password", "Tom")));
public User login(String email, String password) { for (User user : users) { if (user.getEmail().equalsIgnoreCase(email) && user.getPassword().equals(password)) { mailService.sendLoginMail(user); return user; } } throw new RuntimeException("login failed."); }
public User getUser(long id) { return this.users.stream().filter(user -> user.getId() == id).findFirst().orElseThrow(); }
public User register(String email, String password, String name) { users.forEach((user) -> { if (user.getEmail().equalsIgnoreCase(email)) { throw new RuntimeException("email exist."); } }); User user = new User(users.stream().mapToLong(u -> u.getId()).max().getAsLong() + 1, email, password, name); users.add(user); mailService.sendRegistrationMail(user); return user; } }
|
注意到UserService
通过setMailService()
注入了一个MailService
。
然后,我们需要编写一个特定的application.xml
配置文件,告诉Spring的IoC容器应该如何创建并组装Bean:
1 2 3 4 5 6 7 8 9 10 11 12
| <?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="userService" class="com.itranswarp.learnjava.service.UserService"> <property name="mailService" ref="mailService" /> </bean>
<bean id="mailService" class="com.itranswarp.learnjava.service.MailService" /> </beans>
|
- 每个
<bean ...>
都有一个id
标识,相当于Bean的唯一ID;
- 在
userService
Bean中,通过<property name="..." ref="..." />
注入了另一个Bean;
- Bean的顺序不重要,Spring根据依赖关系会自动正确初始化。
把上述XML配置文件用Java代码写出来,就像这样:
1 2 3
| UserService userService = new UserService(); MailService mailService = new MailService(); userService.setMailService(mailService);
|
只不过Spring容器是通过读取XML文件后使用反射完成的。
如果注入的不是Bean,而是boolean
、int
、String
这样的数据类型,则通过value
注入,例如,创建一个HikariDataSource
:
1 2 3 4 5 6 7
| <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="password" /> <property name="maximumPoolSize" value="10" /> <property name="autoCommit" value="true" /> </bean>
|
最后一步,我们需要创建一个Spring的IoC容器实例,然后加载配置文件,让Spring容器为我们创建并装配好配置文件中指定的所有Bean,这只需要一行代码:
1 2 3 4
| UserService userService = context.getBean(UserService.class);
User user = userService.login("bob@example.com", "password");
|
完整的main()
方法如下:
1 2 3 4 5 6 7 8
| public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); UserService userService = context.getBean(UserService.class); User user = userService.login("bob@example.com", "password"); System.out.println(user.getName()); } }
|
ApplicationContext
我们从创建Spring容器的代码:
1
| ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
|
可以看到,Spring容器就是ApplicationContext
,它是一个接口,有很多实现类,这里我们选择ClassPathXmlApplicationContext
,表示它会自动从classpath中查找指定的XML配置文件。
获得了ApplicationContext
的实例,就获得了IoC容器的引用。从ApplicationContext
中我们可以根据Bean的ID获取Bean,但更多的时候我们根据Bean的类型获取Bean的引用:
1
| UserService userService = context.getBean(UserService.class);
|
Spring还提供另一种IoC容器叫BeanFactory
,使用方式和ApplicationContext
类似:
1 2
| BeanFactory factory = new XmlBeanFactory(new ClassPathResource("application.xml")); MailService mailService = factory.getBean(MailService.class);
|
BeanFactory
和ApplicationContext
的区别在于,BeanFactory
的实现是按需创建,即第一次获取Bean时才创建这个Bean,而ApplicationContext
会一次性创建所有的Bean。实际上,ApplicationContext
接口是从BeanFactory
接口继承而来的,并且,ApplicationContext
提供了一些额外的功能,包括国际化支持、事件和通知机制等。通常情况下,我们总是使用ApplicationContext
,很少会考虑使用BeanFactory
。
小结
Spring的IoC容器接口是ApplicationContext
,并提供了多种实现类;
通过XML配置文件创建IoC容器时,使用ClassPathXmlApplicationContext
;
持有IoC容器后,通过getBean()
方法获取Bean的引用。
关于原型对象
Spring默认使用单例模式(单例模式,整个生命周期都只有这一个对象,对象的唯一。),但是如果我们要让一个对象每次被实例化时产生的都是一个新对象,那我们就需要设置Bean的scope=”prototype”
Spring自动装配
属性名:AutoWire 属性值:【ByName,ByType,constructor,default,no】
ByName:自动在容器上下文中寻找,和自己对象Set方法后面的值对应的BeanID,需要保证所有Bean的ID唯一性,并且这个Bean需要和自动注入的Set方法的值一致!
1 2 3
| <bean id="people" class="People" autowire="byName"> <property name="name" value="王某"/> </bean>
|
ByType:自动在容器上下文中寻找,和自己对象属性类型值相同的Bean,这时,我们可以取消ID属性,因为装配过程不再依赖于ID。需要保证所有Bean的Class唯一性,并且这个Bean需要和自动注入的属性的类型一致!
使用注解实现自动装配
关于注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Autowired:根据属性类型进行自动装配,若接口不止一个实现,则按照属性名(因为可以不加set)进行匹配bean @Qualifier:必须配合Autowired使用,可以帮助指定实现类id @Resource:可以根据类型注入,可以根据名称注入 @Value:注入普通类型属性 @Autowired:可以根据类型注入,可以根据名称注入(默认优先进行byType装配) 若接口不止一个实现,则按照属性名(因为可以不加set)进行匹配bean 创建实体类对象,在实体类中对象类型属性上或set方法上加上该注解。 加入该注解后,该属性可以不需要添加set方法 @Qualifier:若Autowired按属性名匹配,则可以配合@Qualifier 注解指定实现类id @Resource: 可以根据类型注入,可以根据名称注入(默认优先进行byName装配) 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配; @Value:注入普通类型属性 代替了<property name="..." value="..."/> 的繁琐操作。
|
使用注解须知:
1.导入约束 context约束
2.配置注解的支持 context:annotation-config
1 2 3 4 5 6 7 8 9 10 11
| <?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上使用!
使用Autowired我们可以不用再编写set方法,前提是这个自动装配的属性在IOC(spring)容器中存在,且符合名字ByName!
科普:
1
| @Nullable 字段标记了这个注解,说明这个字段可以为null;
|
1 2 3 4 5
| false值可以为null @Autowired(required = false) 另外还有@Resource注解(自动通过名字,类型,注入) @Component(组件,放在类上,说明这个类被Spring管理;了,这就是Bean)
|
@Component衍生注解
@Component拥有几个衍生注解,在之后的SpringMVC中会进行三层架构分层
dao(@Repository)
Service(@Service)
controller(@Controller)
四个注解功能是一样的,都是将某个类注册到Spring中,装配Bean
自动装配
1 2 3
| @Autowired不能唯一自动装配注解:自动装配通过类型,名字 如果@Autowired不能唯一自动装配注解,那么就需要@Qualifier(value="")
|
作用域
1 2 3 4 5 6 7
| @Component @Scope("prototype") public class User { @Value("小王") public String name;
}
|
完全使用Java方式配置Spring
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package top.yuanfangwa.Config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import top.yuanfangwa.pojo.USer;
@Configuration @ComponentScan("top.yuanfangwa.pojo") public class Config {
@Bean public USer getBean() { return new USer(); } }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import top.yuanfangwa.Config.Config; import top.yuanfangwa.pojo.USer;
public class Mytset { public static void main(String[] args) { ApplicationContext Context = new AnnotationConfigApplicationContext(Config.class); USer user = (USer) Context.getBean("getBean"); System.out.println(user.getName());
} }
|
这样的配置在Spring boot中随处可见
代理模式
代理模式可以使得原本的操作变得纯粹
aop约束
1 2 3 4 5 6 7 8 9
| <?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"> </beans>
|
Aop 表达式
Aop中expression表达式的表示方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 常见的切面表达式 1 所有公有方法的执行
execution(public * *(..))
2 所有以set开头的公有方法的执行
execution(* set*(..))
3 AccountService接口下的所有方法的执行
execution(* com.LightseaBlue.service.AccountService.*(..))
4 com.LightseaBlue.service包下的所有方法的执行
execution(* com.LightseaBlue.service.*.*(..))
5 com.LightseaBlue.service包及其子包下的所有方法的执行
execution(* com.LightseaBlue.service..*.*(..))
6 匹配com.LightseaBlue.service包下的所有类的所有方法(不含子包)
within(com.LightseaBlue.service.*)
7 com.LightseaBlue.service包和子包的所有方法
within(com.LightseaBlue.service..*)
8 匹配AccountService的代理类(不支持通配符)
this(com.LightseaBlue.service.AccountService)
|
spring整合Mybatis
约束
1 2 3 4 5 6 7 8 9 10 11
| <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" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx https://www.springframework.org/schema/aop/spring-tx.xsd"> </beans>
|
总体来说,Spring负责了我们原本所需要负责的所有事情,我们只需要去设定实体类以及注入所需要的信息。
spring 事务
默认情况下只会回滚RuntimeException
(运行时异常)和Error
(错误),对于普通的 Exception(非运行时异常),它不会回滚。回滚生效也是有范围的,需处于public