目录
Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的非入侵式框架!
使用
1、导入jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
IOC(控制反转)
Spring配置
别名
<!--别名,如果添加了别名,我们也可以使用别名获取这个对象-->
<alias name="user" alias="userscx"/>
Bean的配置
<!--
id: bean的唯一标识,也就是相当于我们学的对象名
class: bean对象所对应的全限定名 : 包名 + 类名
name: 也是别名,而且name可以同时取多个别名,可用多种符号分割(‘,’,’;‘,’ ‘)
-->
<bean id="user" class="com.cf.pojo.User" name="user2,user3;user4 user5">
<property name="name" value="mid小试"/>
</bean>
import
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
- beans.xml
- otherbeans.xml
- applicationContext.xml(核心)
applicationContext.xml
<import resource="beans.xml"/>
<import resource="otherbeans.xml"/>
使用的时候,直接使用总的配置就可以了
使用Java的方式配置Spring
不再使用Spring的xml配置了,全权交给Java来做!
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!
实体类
//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("config")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
JavaConfig配置文件
//这个也会Spring容器托管,注册到容器中,因为他本来就是一个@Component
// Configuration代表这是一个配置类,就和我们之前看的beans.xmL
@Configuration
//等价于<context:component-scan base-package="com.cf"/>
@ComponentScan("com.cf")
//添加多个配置文件,等价于import resource="Myconfig2.xml"/>
@Import(Myconfig2.class)
public class Myconfig {
//注册一个bean .就相当于我们之前写的一个bean标签
//这个方法的名字,就相当于bean标签中的id属性
//这个方法的返回值,就相当于bean标签中的cLass属性
@Bean
public User getuser(){
//返回值就是要注入到bean的对象!
return new User();
}
}
测试
@Test
public void test(){
//如果完全使用了配置类方法去做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载!
ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
User user = context.getBean("getuser", User.class);
System.out.println(user);
}
这种纯Java的配置方式,在SpringBoot中随处可见!
Bean的作用域
1、单例模式(Spring默认机制)
<bean id="hello" class="com.cf.pojo.Hello" scope="singleton">
2、原型模式:每次从容器中get的时候,都会产生一个新对象!
<bean id="hello" class="com.cf.pojo.Hello" scope="prototype">
3、其余的request、session、application、这些个只能在web开发中使用到!
Bean的自动装配
- 自动装配是Spring满足bean依赖一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性!
在Spring中有三种装配的方式
- 在xml中显式配置
- 在java中显式配置
- 隐式的自动装配bean【重要】
实体类
public class People {
private Cat cat;
private Dog dog;
private String name;
public Cat getCat() {return cat;}
public void setCat(Cat cat) {this.cat = cat;}
public Dog getDog() {return dog;}
public void setDog(Dog dog) {this.dog = dog;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
byName自动装配
<bean id="cat" class="com.cf.pojo.Cat"/>
<bean id="dog" class="com.cf.pojo.Dog"/>
<!--byNmae:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean-id-->
<bean id="people" class="com.cf.pojo.People" autowire="byName">
<property name="name" value="眯帝"/>
</bean>
byType自动装配
<bean class="com.cf.pojo.Cat"/>
<bean class="com.cf.pojo.Dog"/>
<!--byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean-->
<bean id="people" class="com.cf.pojo.People" autowire="byType">
<property name="name" value="眯帝"/>
</bean>
小结:
byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!
使用注解实现自动装配
使用
1、导入约束:context约束
xmlns:context=“http://www.springframework.org/schema/context”
2、配置注解的支持: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
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解的支持-->
<context:annotation-config/>
</beans>
@Autowired
直接在属性上使用即可!也可使用在Set方法上使用!
使用Autowired可以不用编写Set方法,前提是这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byName
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
}
拓展
如果显示定义了Autowired的required的属性为false,说明这个对象可以为null,否则不允许为空
等价于@Nullable:字段标记了这个注解,说明这个字段可以为null;
public class People {
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
}
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定唯一的bean对象注入!
public class People {
@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
private Dog dog;
}
@Resource注解【属于JDK】
- @Resource类似于@Autowired
- @Resource(name = “xxx”)类似于@Autowired+@Qualifier(value=“xxx”)
public class People {
@Resource(name = "dog2")
private Cat cat;
@Autowired
private Dog dog;
}
@Resource与@Autowired的区别:
装配顺序不同
- @Resource默认先byName再byType
- @Autowired默认先byType再byName
使用注解开发
在Spring4之后,要使用注解开发,必须要保证aop的包导入了
使用注解需要导入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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.cf"/>
<context:annotation-config/>
</beans>
bean注入
//等价于<bean id="user" class="com.cf.pojo.User"/>
//@Component组件 放在类上,说明这个类被Spring管理了,就是bean!
@Component
public class User {
public String name;
}
属性注入
//等价于<bean id="user" class="com.cf.pojo.User"/>
//@Component组件 放在类上,说明这个类被Spring管理了,就是bean!
@Component
public class User {
//等价于<property name="name" value="mid"/>
@Value("mid")
public String name;
}
衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
- dao【@Repository】
- service【@Service】
- controller【@Controller】
这四个注解功能是一样的,都是代表将某个类注册到Spring容器中,装配bean
自动装配
- @Autowired
- @Resource
- @Nullable
详见,使用注解实现自动装配
作用域
//等价于<bean scope="prototype"/>
@Scope("prototype")
public class User {
//等价于<property name="name" value="mid"/>
@Value("mid")
public String name;
}
了解xml和注解
xml和注解的优缺点
- xml更加万能,适用于任何场合,维护简单方便!
- 注解不是自己类使用不了,维护相对复杂!
xml和注解的最佳实践
- xml用来管理bean
- 注解只负责完成属性的注入
- 在使用的过程中,只需要注意一个问题:要想让注解生效,就需要开启注解的支持
指定要扫描的包,这个包下的注解就会生效
<context:component-scan base-package=“com.cf.pojo”/>
<context:annotation-config />
代理模式
为什么要学代理模式?
因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】
角色分析:
- 抽象角色:一般会使用接口或抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真是角色后,我们会做一些附属操作
- 客户:访问代理对象的人!
代理模式的优缺点
优点:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共也就就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!、
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍开发效率会变低
静态代理
代码步骤:
1、接口
public interface Rent {
public void rent();
}
2、真实角色
//房东
public class Host implements Rent {
public void rent() {
System.out.println("房东要出租房子!");
}
}
3、代理角色
public class Proxy implements Rent {
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
seehouse();
host.rent();
}
//看房
public void seehouse(){
System.out.println("中介带你看房");
}
}
4、客户端访问代理角色
public class Client {
public static void main(String[] args) {
//房东要租房子
Host host = new Host();
//代理,中介帮房东租房子,但是代理角色一般会有一些附属操作!
Proxy proxy = new Proxy(host);
//你不用面对房东,直接找中介即可!
proxy.rent();
}
}
动态代理
动态代理的代理类是动态生成的,不是我们直接写好的!
动态代理分为两大类:基于接口的动态代理、基于类的动态代理
- 基于接口:JDK动态代理
- 基于类:cglib
- java字节码实现:Javassist
需要了解两个类:Proxy【代理(类)】、InvocationHandler【调用处理程序(接口)】
Proxy
就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法
InvocationHandler
是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法
具体使用
步骤一:
创建代理类的接口对象
步骤二:
生成代理类Proxy.newProxyInvocation(参数1.参数2,参数3)
- 参数1:表名你要用那个类加载器去加载生成的代理类。(这个是JVM类加载的知识,可以去了解一下,挺简单)
- 参数2:说明你要生成的代理类的接口
- 参数3:实现了InvocationHandle的类,这个类只有一个方法需要你要实现它
步骤三:
实现InvocationHandler中的invoke方法
- 第一个参数,是生成的代理类,目前没发现用处,不管它。
- 第二个参数,是执行的方法(利用反射的原理,可以去看反射,也很简单。)
- 第三个参数,是执行某方法需要的参数。
其中,
第二个第三个参数解释执行的方法意思是:代理类不是要代理某个对象么,然后增强里面的方法么,指得就是这个方法,代理类会为几乎所有方法都增强,除非你在这里做判断。
返回值,是执行这个方法所返回的值。
步骤四:
在invoke方法中写你要去执行方法,就是用第二参数的invoke(target,args);
- 第一个参数是你要增强的对象。
- 第二个是参数。
- object是你返回的类型
代码实现
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;
}
}