Spring基础
1. Spring
1.1 简介
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.3</version>
</dependency>
1.2 优先
-
Spring开源的容器
-
Spring是轻量级和非入侵式
-
控制反转(IOC),面向切面编程(AOP)
-
支持事务的处理,对框架的整合支持
1.3 组成
1.4 扩展
-
SpringBoot
快速开发,约定大于配置
-
SpringCloud
基于SpringBoot实现
2. IOC理论推导
- UserDao接口
- UserDaoImp接口
- UserService业务接口
- UserServiceImpl业务实现类
使用Set接口实现
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
解决创建对象的问题,耦合性降低
IOC本质:设计思想,DI(依赖注入)是一种实现方式,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">
<!--spring创建对象
UserDaoMysqlImpl mysqlImpl = new UserDaoMysqlImpl()
id = 变量名
class = new 的对象
property 相当于给对象中的属性设置一个值
-->
<bean id = "mysqlImpl" class="com.liu.dao.UserDaoMysqlImpl"/>
<bean id = "oracleImpl" class="com.liu.dao.UserDaoOracleImpl"/>
<bean id = "sqlServermpl" class="com.liu.dao.UserDaoSqlServermpl"/>
<bean id="UserServiceImpl" class="com.liu.service.UserServiceImpl">
<!--ref: 引用spring容器创建的对象
value: 具体的值,基本数据类型
-->
<property name="userDao" ref="sqlServermpl"/>
</bean>
</beans>
测试代码
import com.liu.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userServiceImpl = (UserServiceImpl)applicationContext.getBean("UserServiceImpl");
userServiceImpl.getUser();
}
}
3. IOC创建对象的方式
-
使用无参构造创建对象
<bean id="user" class="com.liu.pojo.User"> <property name="name" value="测试"/> </bean>
-
使用有参构造创建对象
-
下标赋值
<!--1.下标赋值--> <bean id="user" class="com.liu.pojo.User"> <constructor-arg index="0" value="测试"/> </bean>
-
参数类型赋值
<!--2.参数类型赋值,不建议使用--> <bean id="user" class="com.liu.pojo.User"> <constructor-arg type="java.lang.String" value="测试"/> </bean>
-
参数名赋值
<!--3.通过参数名设置--> <bean id="user" class="com.liu.pojo.User"> <constructor-arg name="name" value="测试"/> </bean>
-
总结:在配置文件加载时,容器中管理的对象就创建了
4. Spring配置
4.1 别名
<!--别名,添加别名可以利用别名获取对象-->
<alias name="user" alias="abc"/>
4.2 Bean配置
<!--
id: bean的唯一标识符,对象名
class: bean对象对应的全限定名: 包名+类型
name: 也是别名,可以取多个别名
-->
<bean id="userT" class="com.liu.pojo.UserT" name="user2, u2">
<property name="name" value="12"/>
</bean>
4.3 import
利用import将所有人的配置合并成一个总的配置
<import resource="beans.xml"/>
5. 依赖注入
5.1 构造器注入
参考前面方法
5.2 Set方式注入
依赖:bean对象的创建依赖容器
注入:bean对象的所有属性,由容器来注入
对象
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
实际对象
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String, String> card;
private Set<String> games;
private Properties info;
private String wife;
}
配置项
<?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.liu.pojo.Student">
<!--第一种,普通值注入,value-->
<property name="name" value="测试"/>
</bean>
</beans>
测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student)context.getBean("student");
System.out.println(student.getName());
}
}
注入信息
<?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="address" class="com.liu.pojo.Address"/>
<bean id="student" class="com.liu.pojo.Student">
<!--第一种,普通值注入,value-->
<property name="name" value="测试"/>
<!--第二种,对象注入,ref-->
<property name="address" ref="address"/>
<!--数组注入-->
<property name="books">
<array>
<value>西游记</value>
<value>三国演义</value>
<value>水浒传</value>
<value>红楼梦</value>
</array>
</property>
<!--list注入-->
<property name="hobbys">
<list>
<value>看书</value>
<value>打球</value>
<value>玩游戏</value>
</list>
</property>
<!--map注入-->
<property name="card">
<map>
<entry key="身份证" value="12344555"/>
<entry key="银行卡" value="24124412"/>
</map>
</property>
<!--set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>CS</value>
</set>
</property>
<!--null-->
<property name="wife">
<null/>
</property>
<!--properties-->
<property name="info">
<props>
<prop key="driver">1234</prop>
<prop key="url">男</prop>
<prop key="username">root</prop>
<prop key="password">123445</prop>
</props>
</property>
</bean>
</beans>
5.3 拓展方式注入
p空间和c空间
5.4 bean的作用域
- singleton(单例模式)--默认
- prototype(原型模式)--每次产生新的对象
- 其余的request,session,application,只能在web开发用到
6. Bean的自动装配
三种装配的方式
- xml显示的装配
- 在java中显示的装配
- 隐式的自动装配bean
6.1 测试
6.2 使用xml自动装配
<!--
byName: 会自动在容器上下文差再,和自己对象set方法后面的值对应的beanId(setDog, setCat)
byType: 会自动在容器上下文差再,和自己对象属性类型相同的bean
-->
<bean id="people" class="com.liu.pojo.People" autowire="byName">
<property name="name" value="无敌"/>
</bean>
总结:
- byName需要所有的beanId唯一
- byType需要所有的bean的class唯一
6.3 使用注解自动装配
使用注解:
-
导入约束
-
配置注解的支持
<context:annotation-config/>
<?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 http://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>
@Autowire
直接在属性上使用,也可也在set方法使用,可以省略set方法
@Nullable 字段标记这个注解,说明这个字段可以为Null
public @interface Autowired {
boolean required() default true;
}
测试代码
public class People {
//如果显示定义Autowired的required属性为false,说明这个对象可以为null,否则不允许为null
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;
}
如果装配的环境比较复杂,自动装配无法通过一个注解完成,此时可以采用@Qualifer去配合@Autowired(使用byType)使用,指定一个唯一的bean对象注入
public class People {
@Autowired
@Qualifier(value = "cat111")
private Cat cat;
@Autowired
@Qualifier(value = "dog222")
private Dog dog;
}
@Resource注解
public class People {
@Resource(name = "cat11")
private Cat cat;
@Resource
private Dog dog;
小结:
@Resource和@Autowired
- 都是可以自动装配,都可以放在属性字段上
- @Autowired通过byType实现,要求对象存在
- @Resource通过byName,如果找不到使用byType实现,两个都找不到报错
7. 使用注解开发
注解开发,必须保证AOP包导入
需要导入context约束
-
bean
-
属性注入
// 等价于 <bean id = "user" class = "com.liu.pojo.User"/> // @Component 组件 @Component public class User { public String name; // 相当于 <property name = "name" value = "测试"/> @Value("测试") public void setName(String name) { this.name = name; } }
-
衍生的注解
@Component有几个衍生注解,按照mvc三层架构分层
-
dao 【@Repository】
-
service 【@Service】
-
controller 【@Controller】
这四个注解的功能一样,将类注册到spring中,装配Bean
-
-
作用域
@Component @Scope("prototype") public class User { public String name; // 相当于 <property name = "name" value = "测试"/> @Value("测试") public void setName(String name) { this.name = name; } }
-
小结
xml与注解:
- xml更加万能,适用于任何场合,维护方便
- 注解不是自己类使用不了,维护相对复杂
xml与注解:
- xml用来管理Bean
- 注解只负责完成属性的注入
- 注解需要开启注解的支持,扫描器的开启
8. 使用Java的方式配置Spring
使用Java来进行配置,JavaConfig在Spring4之后,推荐使用
实体类
public class User {
private String name;
public String getName() {
return name;
}
@Value("测试")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置文件
// 配置类也注册到容器中,类似于之前的xml文件
@Configuration
@ComponentScan("com.liu.pojo")
@Import(MyConfig2.class)
public class MyConfig {
//注册一个bean,就相当于我们之前的一个bean标签
//方法的名字就是Bean的id
//返回对象为Bean的class
@Bean
public User getUser() {
return new User();
}
}
测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User)context.getBean("getUser");
System.out.println(user.getName());
}
}
这种纯java配置,在springboot很常见
9. 代理模式
代理模式分类:
- 静态代理
- 动态代理
9.1 静态代理
角色分析:
- 抽象角色:接口或者抽象类解决
- 真实角色:被代理的角色
- 代理角色:代理真实对象,代理真实角色后,一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤:
-
接口
//租房的接口 public interface Rent { public void rent(); }
-
真实角色
//房东 public class Host implements Rent{ public void rent() { System.out.println("房东要出租房子"); } }
-
代理角色
public class Proxy implements Rent{ private Host host; public Proxy(Host host) { this.host = host; } public Proxy() { } public void rent() { host.rent(); } //看房 public void seeHouse() { System.out.println("中介带你看房"); } //收中介费 public void fare() { System.out.println("中介收中介费"); } //签合同 public void signContract() { System.out.println("签约租赁合同"); } }
-
客户访问代理角色
public class Client { public static void main(String[] args) { //房东要租房子 Host host = new Host(); //代理,中介帮房东租房子,代理角色有一些附属操作 Proxy proxy = new Proxy(host); //你不用面对房东,直接找中介租房 proxy.rent(); } }
代理模式的好处:
- 可以使真实角色操作更加纯粹,不用关注一些公共的业务
- 公共交给代理角色,实现业务的分工
- 公共业务扩展,方便集中管理
缺点:
- 一个真实角色就会产生代理角色,代码翻倍,开发效率降低
10.2 动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类动态生产,不是直接写好
- 动态代理分为两类:基于接口的动态代理,基于类的动态代理
- 基于接口---JDK动态代理
- 基于类:cglib
- java字节码实现:javasist
了解两个类:Proxy:代理,InvocationHandler:调用处理程序
InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是使用反射机制实现
Object result = method.invoke(target, args);
return result;
}
}
动态代理的好处:
- 可以使真实角色操作更加纯粹,不用关注一些公共的业务
- 公共交给代理角色,实现业务的分工
- 公共业务扩展,方便集中管理
- 一个动态代理类代理的是一个接口,一般是对应的一类业务
- 一个动态代理类可以代理多个类,只有实现同一个接口即可