Spring
1、Spring
简介
spring整合了现有的技术框架。
导入依赖包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.15</version>
</dependency>
优点(特点)
- 开源的免费框架(容器)
- 轻量级、非入侵式(不改变原有代码)的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务的处理,对框架整合的支持
2、IOC(控制反转)理论的理解
传统开发流程:
- UserDao接口
public interface UserDao {
void getUser();
}
- UserDaoImpl
public class UserDaoImpl implements UserDao{
public void getUser() {
System.out.println("获取UserDao对象");
}
}
- UserService
public interface UserService {
void getUser();
}
- UserServiceImpl
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoImpl();
public void getUser() {
userDao.getUser();
}
}
- 测试
public class MyTest {
public static void main(String[] args) {
//用户实际调用的是业务层,dao层他们不需要接触
UserServiceImpl userService = new UserServiceImpl();
userService.getUser();
}
}
当用户改变了需求时,(使用UserDaoMysqlImpl 需求)
public class UserDaoMysqlImpl implements UserDao{
public void getUser() {
System.out.println("MySql获取User");
}
}
需要程序员根据用户需求去改变业务层代码(如果程序庞大,这需要程序员花费昂贵的代价修改原来的代码)
public class UserServiceImpl implements UserService{
//改变了实现的接口
private UserDao userDao = new UserDaoMysqlImpl();
public void getUser() {
userDao.getUser();
}
}
使用set实现动态的改变接口
public class UserServiceImpl implements UserService{
private UserDao userDao;
//使用set,使得可以动态的改变接口
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
}
使得用户可以自己选择需求
public class MyTest {
public static void main(String[] args) {
//用户实际调用的是业务层,dao层他们不需要接触
UserServiceImpl userService = new UserServiceImpl();
//用户选择实现哪个接口
userService.setUserDao(new UserDaoMysqlImpl());
userService.getUser();
}
}
- 之前是程序员主动创建对象,控制权在程序员手上
- 使用set之后,程序不再具有主动性,而是变成了被动的接受对象
这种思想,从本质上解决了问题,程序员不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专注在业务的实现上。这就是IOC(控制反转)的原型!
IOC(Inversion of Control)本质:控制反转是一种设计思想,在没有IOC的程序中,对象的创建与对象之间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方。
控制反转通过描述(XML或注解)经由第三方去生产或获取特定对象。在spring中实现控制反转的是IOC容器,其实现方式是依赖注入(Dependency Injection,DI)。
3、hello spring
导入依赖包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.15</version>
</dependency>
创建实体类
public class Hello {
private String str;
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
Spring 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">
<bean id="hello" class="pojo.Hello">
<property name="str" value="spring"/>
</bean>
</beans>
实例化容器,并使用容器
public class MyTest {
public static void main(String[] args) {
//实例化容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用容器
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.getStr());
}
}
以上程序中:
- hello对象是由spring容器创建,
- hello对象的属性值是spring容器设置的
这个过程就是控制反转
控制:使用spring后,对象是由springIOC容器创建
反转:程序本身不创建对象,而变成被动的接收对象
依赖注入:就是使用set方法来进行注入
现在,我们彻底不用再去修改程序,要实现不同的操作,只需在xml配置文件中进行修改
使用spring修改之前的传统开发
<?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="defaultImpl" class="dao.UserDaoImpl"/>
<bean id="MysqlImpl" class="dao.UserDaoMysqlImpl"/>
<!--根据需要修改ref-->
<bean id="service" class="service.UserServiceImpl">
<property name="userDao" ref="defaultImpl"/>
</bean>
</beans>
public class MyTest {
public static void main(String[] args) {
/*
//用户实际调用的是业务层,dao层他们不需要接触
UserServiceImpl userService = new UserServiceImpl();
//用户选择实现哪个接口
userService.setUserDao(new UserDaoMysqlImpl());
userService.getUser();
*/
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl service = (UserServiceImpl) context.getBean("service");
service.getUser();
}
}
4、IOC创建对象的方式
- IOC容器创建对象时,自动调用了对象的无参构造函数
- 创建对象时,创建了该配置文件中的所有对象,无论有没有被使用
- 在配置文件加载的时候,容器中的对象就已经初始化了
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
- 下标赋值
<bean id="user" class="pojo.User">
<constructor-arg index="0" value="张3"/>
</bean>
- 类型赋值(不建议使用)
<bean id="user" class="pojo.User">
<constructor-arg type="java.lang.String" value="张3"/>
</bean>
- 参数名赋值
<bean id="user" class="pojo.User">
<constructor-arg name="name" value="张3"/>
</bean>
5、spring配置
5.1 别名
- 使用alias标签
<alias name="user" alias="u1"/>
<bean id="user" class="pojo.User">
<constructor-arg name="name" value="张3"/>
</bean>
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用别名
User user = (User) context.getBean("u1");
System.out.println(user.getName());
}
}
- 在bean中使用name属性(推荐使用)
<!--空格 分号 逗号都可以当作分割-->
<bean id="user" class="pojo.User" name="u2 u3;u4,u5">
<constructor-arg name="name" value="张3"/>
</bean>
5.2 在配置文件中使用其他配置文件
applicationContext.xml
<import resource="beans.xml"/>
6、依赖注入
依赖注入:本质上就是set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
构造器注入
<bean id="user" class="pojo.User">
<constructor-arg name="name" value="张3"/>
</bean>
set方式注入(重点)
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbies;
private Map<String,String> card;
private Set<String> games;
private Properties info;
......
}
<?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 class="pojo.Address" id="address">
<property name="address" value="xian"/>
</bean>
<bean id="student" class="pojo.Student">
<!--普通属性,value-->
<property name="name" value="张三"/>
<!--bean注入,ref-->
<property name="address" ref="address"/>
<!--数组-->
<property name="books">
<array>
<value>西游记</value>
<value>红楼梦</value>
<value>水浒传</value>
<value>金瓶梅</value>
</array>
</property>
<!--list-->
<property name="hobbies">
<list>
<value>上网吧</value>
<value>找妹子</value>
<value>打游戏</value>
</list>
</property>
<!--map-->
<property name="card">
<map>
<entry key="id" value="1814040123"/>
<entry key="name" value="小明"/>
</map>
</property>
<!--set-->
<property name="games">
<set>
<value>lol</value>
<value>索尼子</value>
</set>
</property>
<!--properties-->
<property name="info">
<props>
<prop key="driver">com.mysql.jdbc.Driver</prop>
<prop key="username">root</prop>
<prop key="password">123</prop>
</props>
</property>
</bean>
</beans>
扩展方式注入
- p命名空间(property)
使用时需要导入xml约束xmlns:p="http://www.springframework.org/schema/p"
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="pojo.User" p:username="张三" p:age="18" p:sex="1"/>
</beans>
- c命名空间(constructs-arg)
需要相对应的构造函数才能使用
使用时需要导入xml约束xmlns:c="http://www.springframework.org/schema/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: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="user2" class="pojo.User" c:username="李四" c:age="20" c:sex="0"/>
</beans>
7、Bean作用域
单例模式(默认)
在IOC容器中创建一个对象,所有地方共用这一个对象
<!--不写默认为singleton-->
<bean id="user" class="pojo.User" p:username="张三" p:age="18" p:sex="1" scope="singleton"/>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
User user = context.getBean("user", User.class);
User user2 = context.getBean("user", User.class);
System.out.println(user==user2); //true
}
原型模式
每次从容器中get时都会产生一个新对象
<bean id="user" class="pojo.User" p:username="张三" p:age="18" p:sex="1" scope="prototype"/>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
User user = context.getBean("user", User.class);
User user2 = context.getBean("user", User.class);
System.out.println(user==user2); //false
}
其他的(request、session、application、websocket只在web开发中用到)
8、自动装配Bean
之前的bean注入,需要自己注入属性值
<bean id="dog" class="Dog"/>
<bean id="cat" class="Cat"/>
<bean id="people" class="People" >
<property name="dog" ref="dog"/>
<property name="cat" ref="cat"/>
</bean>
byName
- 使用byName,需要保证所有bean的id唯一,并且id要和自动注入的属性的set方法的值一致
<!--如果id改为dog1则报错-->
<bean id="dog" class="pojo.Dog"/>
<bean id="cat" class="pojo.Cat"/>
<bean id="people" class="People" autowire="byName"/>
byType
- 使用byType,需要保证所有bean的class唯一,并且class要和自动注入的属性的类型一致
<!--如果id改为dog1也能正确运行-->
<bean id="dog" class="pojo.Dog"/>
<bean id="cat" class="pojo.Cat"/>
<bean id="people" class="People" autowire="byType"/>
使用注解实现自动装配
- 使用注解的方式要优于使用xml配置文件
使用注解:
- 导入xml约束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
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>
- 配置注解的支持
<context:annotation-config/>
@Autowired
注解:
- 默认通过byType的方式实现,如果有多个类型相同,则通过byName方式实现
- 直接在属性上使用@Autowired,也可以在set方法上使用
@Autowired(required = false)
表示这个对象可以为null,默认为true不允许为空
<context:annotation-config/>
<bean id="people" class="People"/>
<bean id="cat" class="Cat"/>
<bean id="dog" class="Dog"/>
public class People {
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
......
}
如果@Autowired自动装配的环境比较复杂,无法通过一个注解完成的时候,可以使用 @Qualifier
去配置@Autowired,指定唯一的bean注入。
<context:annotation-config/>
<bean id="people" class="People"/>
<bean id="cat1" class="Cat"/>
<bean id="cat2" class="Cat"/>
<bean id="dog1" class="Dog"/>
<bean id="dog2" class="Dog"/>
public class People {
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
@Autowired
@Qualifier(value = "dog1")
private Dog dog;
......
}
@Resource
注解:
- 默认通过byName实现,如果找不到,则通过byType实现
public class People {
@Resource(name = "cat1")
private Cat cat;
@Autowired
@Qualifier(value = "dog1")
private Dog dog;
....
}
9、使用注解开发
使用注解开发需要导入AOP包,并在xml中增加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
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>
- 装配bean
<!--自动扫描包,将pojo包中的带有组件注解的类装配-->
<context:component-scan base-package="com.pojo"/>
@Component
public class User {
private String name;
- 属性的注入
@Component
public class User {
//相当于<property name="name" value="张三"/>
@Value("张三")
private String name;
-
@Component衍生的注解
@Component 有几个衍生的注解,在web开发中会按照MVC三层架构分层- dao层:@Repository
- service层:@Service
- controller层:@Controller
这四个注解的作用是一样的,都代表将某个类注册到spring中,装配bean
-
作用域
@Controller
//相当于<bean id="user" class="com.pojo.User" scope="prototype">
@Scope("prototype")
public class User {
@Value("张三")
private String name;
- 小结
xml与注解:- xml更加万能,适用于任何场合,维护相对简单
- 注解维护相对复杂
- 最佳实践:xml用来管理bean,注解只负责属性的注入
- 使用时要注意开启注解支持
<context:annotation-config/>
10、使用Java的方式配置spring
在spring boot中常用
java配置类:
@Configuration
public class MyConfig2 {
@Bean
@Scope("prototype")
public Student getStudent(){
return new Student();
}
}
@Configuration
@ComponentScan("com.pojo")
@Import(MyConfig2.class)
public class MyConfig {
@Bean
public User user(){
return new User();
}
}
实体类:
@Component
public class User {
@Value("张三")
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
@Component
public class Student {
@Value("18")
private int age;
@Override
public String toString() {
return "Student{" +
"age=" + age +
'}';
}
}
测试:
public class Test04 {
@Test
public void test(){
//使用Java配置类的方式,就只能通过AnnotationConfigApplicationContext来获取容器,通过配置类的class对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.getBean("user",User.class);
System.out.println(user);
Student student1 = (Student) context.getBean("getStudent");
Student student2 = (Student) context.getBean("getStudent");
System.out.println(student1==student2); //false
}
}
以上来自看kuangshen视频的笔记