Powered By JamPang
QQ:847885907
https://jampang.cn/
Spring笔记
其他部分访问
6、依赖注入
项目名:spring-04-di
- 依赖注入(Dependency Injection,DI)。
- 依赖 : 指Bean对象的创建依赖于容器,Bean对象的依赖资源。
- 注入 : 指Bean对象所依赖的资源,由容器来设置和装配。
6.1、构造器注入
之前说过,参考第四点。
<bean id="user" class="com.jam.pojo.User">
<constructor-arg name="name" value="jam"/>
</bean>
6.2、Set方式注入【重点】
依赖注入:Set注入!
- 依赖:bean对象的创建依赖于容器!
- 注入:bean对象中的所有属性,由容器来注入!
【环境搭建】
复杂类型 Address.java
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
真实测试对象 Student.java
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; public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String[] getBooks() { return books; } public void setBooks(String[] books) { this.books = books; } public List<String> getHobbys() { return hobbys; } public void setHobbys(List<String> hobbys) { this.hobbys = hobbys; } public Map<String, String> getCard() { return card; } public void setCard(Map<String, String> card) { this.card = card; } public Set<String> getGames() { return games; } public void setGames(Set<String> games) { this.games = games; } public Properties getInfo() { return info; } public void setInfo(Properties info) { this.info = info; } public String getWife() { return wife; } public void setWife(String wife) { this.wife = wife; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address=" + address + ", books=" + Arrays.toString(books) + ", hobbys=" + hobbys + ", card=" + card + ", games=" + games + ", info=" + info + ", wife='" + wife + '\'' + '}'; } }
beans.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="student" class="com.jam.pojo.Student"> <!--第一种,普通值注入,value--> <property name="name" value="Jam"/> </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()); } }
6.2.1完善注入信息[value/ref/array/list/map/set/null/properties]
<?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="address" class="com.jam.pojo.Address">
<property name="address" value="江西"/>
</bean>
<bean id="student" class="com.jam.pojo.Student">
<!--第一种,普通值注入,value-->
<property name="name" value="Jam"/>
<!--第二种,Bean注入,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="111111222222223333"/>
<entry key="银行卡" value="6222021505001111111"/>
</map>
</property>
<!-- Set -->
<property name="games">
<set>
<value>LOL-英雄联盟</value>
<value>COC-部落冲突</value>
<value>BOB-球球大作战</value>
</set>
</property>
<!-- null -->
<property name="wife">
<null/>
</property>
<!-- Properties -->
<property name="info">
<props>
<prop key="学号">20200527</prop>
<prop key="性别">男</prop>
<prop key="姓名">旧梦</prop>
</props>
</property>
</bean>
</beans>
6.3、拓展方式注入
我们可以使用p命名空间和c命名空间进行注入
p命名包
xmlns:p="http://www.springframework.org/schema/p"
c命名包
xmlns:c="http://www.springframework.org/schema/c"
beans.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:p="http://www.springframework.org/schema/p"
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">
<!-- p命名空间注入,可以直接注入属性的值:property -->
<bean id="user" class="com.jam.pojo.User" p:name="Jam" p:age="18"/>
<!-- c命名空间注入,通过构造器注入:construct-args -->
<bean id="user2" class="com.jam.pojo.User" c:name="Tim" c:age="18"/>
</beans>
测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user",User.class);
System.out.println(user);
}
@Test
public void test3(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2",User.class);
System.out.println(user);
}
注意:p命名和c命名空间不能直接使用,需要导入xml约束!
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
6.4、Bean的作用域
Scope | Description |
---|---|
singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
prototype | Scopes a single bean definition to any number of object instances. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
单例模式(Spring默认机制)
bean id="user2" class="com.jam.pojo.User" c:name="Tim" c:age="18" scope="singleton"/>
原型模式:每次从容器中get的时候,都会产生一个新的对象!
bean id="user2" class="com.jam.pojo.User" c:name="Tim" c:age="18" scope="prototype"/>
- 其余的request、session、application这些只能在web开发中使用到!
7、Bean的自动装配
项目名:spring-05-Autowired
- 自动装配是Spring满足bean依赖的一种方式。
- Spring会在上下文中自动寻找,并自动给bean装配属性!
在Spring中有三种装配的方式
- 在xml中显示的配置
- 在java中显示配置
- 隐式的自动装配【重点】
7.1、测试
环境搭建:一个人有两个宠物!
Cat.java
public class Cat {
public void shout(){
System.out.println("miao");
}
}
Dog.java
public class Dog {
public void shout(){
System.out.println("wang");
}
}
People.java
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 + '\'' +
'}';
}
}
beans.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="cat" class="com.jam.pojo.Cat"/>
<bean id="dog" class="com.jam.pojo.Dog"/>
<bean id="people" class="com.jam.pojo.People">
<property name="name" value="Jam"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
Test.java
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people", People.class);
people.getDog().shout();
people.getCat().shout();
}
7.2、ByName自动装配
bean.xml
<!--
byName:会自动在容器上下文中查找和自己对象set方法后面的值对应的beanid
-->
<bean id="cat" class="com.jam.pojo.Cat"/>
<bean id="dog" class="com.jam.pojo.Dog"/>
<bean id="people" class="com.jam.pojo.People" autowire="byName">
<property name="name" value="Jam"/>
</bean>
7.3、ByType自动装配
<!--
byType:会自动在容器上下文中查找和自己对象属性类型相同的bean
-->
<bean class="com.jam.pojo.Cat"/>
<bean class="com.jam.pojo.Dog"/>
<bean id="people" class="com.jam.pojo.People" autowire="byType">
<property name="name" value="Jam"/>
</bean>
7.4、byName/byType小结
- byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
- byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
7.5、使用注解实现自动装配
jdk1.5支持的注解,spring从2.5支持注解。
The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML. The short answer is “it depends.”
要使用注解须知:
- 导入约束 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>
@Autowired
直接在属性上使用即可,也可以在Set方式上使用!
使用Autowired我们可以不用编写Set方法了,前提是你的自动装配的属性在IOC(Spring)容器存在,且符合名字byname!
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为null
public @interface AutoWired{
boolean required() default true;
}
测试代码
//如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qulifier(value="XXX")去配置@Autowired的使用,指定一个唯一的bean对象注入!
@Autowired
@Qulifier(value="cat111")
private Cat cat;
@Autowired
@Qulifier(value="222")
private Dog dog;
private String name;
@Resource注解
public class People(){
@Resource(name="cat2")
private Cat cat;
@Resource
private Dog dog;
}
7.6、注解小结
@Resource和@Autowired的区别:
- 都是用来自动装配的,都可以放在属性字段上。
- @Autowired通过byType的方式现实
- @Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现。如果两个都找不到的情况下就报错。【常用】
- 执行顺序不同:@Autowired通过byType的方式现实,@Resource默认通过byName的方式实现
8、使用注解开发
在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
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>
8.1、bean
<!-- 指定要扫描的包,这个包下的注解就会生效 -->
<context:component-scan base-package="com.jam"/>
<context:annotation-config/>
8.2、属性如何注入
//等价于 <bean id="user" class="com.jam.pojo.User">
//@Component组件
@Component
public class User {
//public String name = "Jam";
// @Value("Jam")
// 相当于 <property name="name" value="Jam"/>
@Value("Jam")
public String name;
}
8.3、衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层。
- dao【@Repository】
- service【@Service】
controller【@Controller】
四个注解都是代表将某个类注册到Spring中,装备Bean。
自动装配
@Autowired:自动装配通过类型、名字 如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx") @Nullable:字段标记了这个注解,说明这个字段可以为null @Resource:自动装配通过类型、名字
作用域
package com.jam.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; //等价于 <bean id="user" class="com.jam.pojo.User"> //@Component组件 @Component @Scope("prototype") public class User { //public String name = "Jam"; // @Value("Jam") // 相当于 <property name="name" value="Jam"/> @Value("Jam") public String name; }
8.4、小结
xml与注解:
- xml更加万能,适用于任何场合,维护简单方便。
- 注解 不是自己的类使用不了,维护相对复杂!
xml与注解最佳实践:
- xml用来管理bean
- 注解只负责完成属性的注入
我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持
<!-- 指定要扫描的包,这个包下的注解就会生效 --> <context:component-scan base-package="com.jam"/> <context:annotation-config/>
9、使用Java的方式配置Spring
项目名:spring-07-appconfig
我们现在要完全不适用Spring的xml配置,全权交给Java来做。
JavaConfig是Spring的一个子项目,在Spring 4之后,它就成为了一个核心功能。
User类
package com.jam.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//这个注解的意思就是说嘛这个类被Spring接管了,注册到了容器中。
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("Jam")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
JamConfig.java 配置文件/配置类
package com.jam.config;
import com.jam.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
//这个也会被Spring容器接管,注册到容器中。
//@Configuration代表这个是一个配置类,就等于beans.xml
@Configuration
@ComponentScan("com.jam.pojo")
@Import(JamConfig2.class)
public class JamConfig {
//注册一个bean,就相当于一个bean标签
//方法的名字相当于bean中的id
//返回值相当于bean标签中class属性
@Bean
public User getUser(){
return new User();
}
}
Test.java 测试类
import com.jam.config.JamConfig;
import com.jam.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
public static void main(String[] args) {
//如果完全使用了配置类去做,就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载。
ApplicationContext context = new AnnotationConfigApplicationContext(JamConfig.class);
User user = context.getBean("user",User.class);
System.out.println(user.getName());
}
}
这种纯Java的配置方式,在SpringBoot中随处可见!
10、代理模式
项目:spring-08-proxy
为什么要学习代理模式?因为这就是SpringAOC的底层!【SpringAOC 和 SpringMVC】
代理模式的分类:
- 静态代理
- 动态代理
10.1、静态代理
项目:spring-08-proxy-demo1
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决。
- 真是角色:被代理的角色。
- 代理角色:代理真实觉得,代理真实角色后,我们一般会做一些附属操作。
- 客户:访问代理对象的人。
代码步骤:
接口 Rent.java
//租房 public interface Rent { public void rent(); }
真实角色 Landlord.java
//房东 public class Landlord implements Rent { public void rent() { System.out.println("房东要出租房子"); } }
代理角色 Proxy.java
public class Proxy { private Landlord landlord; public Proxy() { } public Proxy(Landlord landlord) { this.landlord = landlord; } public void rent(){ landlord.rent(); seeHouse(); fee(); sign(); } //看房 public void seeHouse(){ System.out.println("中介带你看房"); } //收中介费 public void fee(){ System.out.println("中介费用"); } //租赁合同 public void sign(){ System.out.println("合同"); } }
客户端访问代理角色 Client.java
public class Client { public static void main(String[] args) { //房东 Landlord landlord = new Landlord(); //代理 中介帮房东出租房子,代理角色一般会有附属操作 Proxy proxy = new Proxy(landlord); //不用面对房东,直接找中介 proxy.rent(); } }
- 可以使用真实角色的操作更加纯粹,不用去关注一些公共的业务。
- 公共也就交给代理角色,实现了业务的分工。
- 公共业务发生扩展的时候,方便集中管理。
缺点:
- 一个真实角色就会产生一个代理角色,代码量翻倍,开发效率变低。
10.2、加深理解
项目:spring-08-proxy-demo1
聊聊AOP
10.3、动态代理
项目:spring-08-proxy-demo3
- 动态代理和静态代理角色一样。
- 动态代理的代理类是动态生成的,不是我们直接写好的。
动态代理分为两个类:基于接口的动态代理 和 基于类的动态代理
- 基于接口的----JDK代理
- 基于类:cglib
- java字节码实现:javasist
需要了解两个类
- Proxy:代理
- InvocationHandler:调用处理程序
newProxyInstance:
ClassLoader:类加载器 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法 Class[]:字节码数组 它是用于让代理对象和被代理对象有相同方法。固定写法。 InvocationHandler:用于提供增强的代码 它是让我们写我们代理。我们一般都是写一个该接口的现实类,通常情况下都是匿名内部类,但不是不许的。 此接口的实现类都是谁用谁写。
动态代理的好处:
- 可以使用真实角色的操作更加纯粹,不用去关注一些公共的业务。
- 公共也就交给代理角色,实现了业务的分工。
- 公共业务发生扩展的时候,方便集中管理。
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务。
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可。
Client.java
package com.jam.demo03;
public class Client {
public static void main(String[] args) {
//真实角色
Landlord landlord = new Landlord();
//代理角色,现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口
pih.setRent(landlord);
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
Landlord.java
//房东
public class Landlord implements Rent {
public void rent() {
System.out.println("房东要出租房子");
}
}
ProxyInvocationHandler.java
//我们用这个类,自动生成代理类。
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
//处理代理实例并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//动态代理饿本质,就是使用反射机制实现。
Object result = method.invoke(rent, args);
fare();
return result;
}
public void seeHouse(){
System.out.println("中介带看房子");
}
public void fare(){
System.out.println("中介费用");
}
}
Rent.java
//租房
public interface Rent {
public void rent();
}
10.3.1、cglib动态代理
导包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
基于子类的动态代理:Enhancer
如何创建代理对象:使用Enhancer类中的create方法
创建代理对象的要求:被代理类不能是最终类
Class:字节码
它是用于指定被代理对象的字节码
Callback:用于提供增加的代码
下一章:点击访问
本文共 2216 个字数,平均阅读时长 ≈ 6分钟
评论 (0)