3.2 根据属性名注入(推荐)
实体类User
该实体类有各种各样的类型,我们尝试对这些复杂数据类型进行注入
@Data @AllArgsConstructor @NoArgsConstructor public class User { 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 girlFriend; }
XML注入
一共八种数据类型,分别加以注入
<!--propertyNameAddress 根据属性名注入的address--> <!--singleton 单例模式 prototype原型模式--> <bean id="address" class="zwz.pojo.Address" scope="singleton"> <property name="address" value="浙江宁波"/> </bean> <bean id="user" class="zwz.pojo.User"> <!-- 第一种 普通值注入--> <property name="name" value="ZWZ"></property> <!-- 第二种 Bean注入--> <property name="address" ref="address"></property> <!-- 第三种 数组--> <property name="books"> <array> <value>Java程序设计</value> <value>JavaScript程序设计</value> <value>Python程序设计</value> </array> </property> <!-- 第四种 List--> <property name="hobbys"> <list> <value>敲代码</value> <value>听歌</value> <value>看小说</value> </list> </property> <!-- 第五种 Map--> <property name="card"> <map> <entry key="card01" value="卡片1"></entry> <entry key="card02" value="卡片2"></entry> <entry key="card03" value="卡片3"></entry> </map> </property> <!-- 第六种 Set--> <property name="games"> <set> <value>QQ飞车手游</value> <value>DNF</value> <value>敲代码</value> </set> </property> <!-- 第七种 空值--> <property name="girlFriend"> <null></null> </property> <!-- 第八种 配置--> <property name="info"> <props> <prop key="id">0413170337</prop> <prop key="tel">17857054388</prop> </props> </property> </bean>
测试
@Test public void testPropertyName() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = (User) context.getBean("user"); System.out.println(user); }
可以看出,八种数据类型,都已经成功注入到对象中
Student{ name='ZWZ' , address=Address(address=浙江宁波) , books=[Java程序设计, JavaScript程序设计, Python程序设计] , hobbys=[敲代码, 听歌, 看小说] , card={card01=卡片1, card02=卡片2, card03=卡片3} , games=[QQ飞车手游, DNF, 敲代码] , info={tel=17857054388, id=0413170337} , girlFriend='null'} Process finished with exit code 0
3.3 根据构造器下标注入
@AllArgsConstructor 注解在之间提到过,是生成全部参数的构造方法
我们可以通过构造器的参数,实现值注入
比如对于构造器0号下标的bookName,注入Java程序设计…
实体类Book
@Data @AllArgsConstructor @NoArgsConstructor public class Book { private String bookName; private int pageNum; }
XML注入
<bean id="bookIndex" class="zwz.pojo.Book"> <constructor-arg index="0" value="Java程序设计"/> <constructor-arg index="1" value="66"/> </bean>
测试
@Test public void testConstructorIndex() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Book book = (Book) context.getBean("bookIndex"); System.out.println(book); }
Book(bookName=Java程序设计, pageNum=66) Process finished with exit code 0
3.4 根据构造器数据类型注入
@AllArgsConstructor 注解在之间提到过,是生成全部参数的构造方法
我们可以通过构造器的参数类型,实现值注入
比如对于构造器的String类型变量bookName,注入Java程序设计…
当然构造器**不同参数的数据类型必须不一致,否则会出错**,所以这个注入方式说实话没什么用…
### 实体类Book
@Data @AllArgsConstructor @NoArgsConstructor public class Book { private String bookName; private int pageNum; }
XML注入
<bean id="bookType" class="zwz.pojo.Book"> <constructor-arg type="java.lang.String" value="Java程序设计"/> <constructor-arg type="int" value="66"/> </bean>
测试
@Test public void testConstructorType() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Book book = (Book) context.getBean("bookType"); System.out.println(book); }
Book(bookName=Java程序设计, pageNum=66) Process finished with exit code 0
3.5 C / P 命名空间注入
c命名空间注入或者p命名空间注入,分别利用变量名称和构造器下标进行值注入,和之前的没有本质性的区别
XML属性名注入、Java注解最为常用,而C/P命名空间注入一般不会去使用…
实体类Book
@Data @AllArgsConstructor @NoArgsConstructor public class Book { private String bookName; private int pageNum; }
XML注入
<bean id="cBook" class="zwz.pojo.Book" c:_0="Java程序设计" c:_1="66"/> <bean id="pBook" class="zwz.pojo.Book" p:bookName="Java程序设计" p:pageNum="66"/>
测试
@Test public void testC() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Book book = (Book) context.getBean("cBook"); System.out.println(book); } @Test public void testP() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Book book = (Book) context.getBean("pBook"); System.out.println(book); }
输出相同
Book(bookName=Java程序设计, pageNum=66) Process finished with exit code 0
3.6 单例/原型模式
XML配置
singleton 为单例模式,从容器中多次获取bean,bean相同
prototype为原型模式,从容器中多次获取bean,bean不同
<bean id="singletonBook" class="zwz.pojo.Book" scope="singleton"> <property name="bookName" value="Java程序设计"/> <property name="pageNum" value="66"/> </bean> <bean id="prototypeBook" class="zwz.pojo.Book" scope="prototype"> <property name="bookName" value="Java程序设计"/> <property name="pageNum" value="66"/> </bean>
测试
@Test public void testScope(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Book book1 = (Book) context.getBean("singletonBook"); Book book2 = (Book) context.getBean("singletonBook"); System.out.println("singletonBook: " + (book1 == book2)); Book book3 = (Book) context.getBean("prototypeBook"); Book book4 = (Book) context.getBean("prototypeBook"); System.out.println("prototypeBook: " + (book3 == book4)); }
``` singletonBook: true prototypeBook: false ```
3.7 自动装配
自动装配的意思是,假设有两个实体Bean A、B
如果A中包含B,且B已经配置在容器中,那么A可以通过自动装配,将B自动装配注入
当然自动装配只对引用变量生效,对于int、String不支持
实体类BookUser
定义一个包含Book的实体类BookUser
@Data @AllArgsConstructor @NoArgsConstructor public class BookUser { Book book; }
### XML配置
<bean id="book" class="zwz.pojo.Book"> <property name="bookName" value="自动装配的BookName"/> <property name="pageNum" value="66"/> </bean> <!-- - byName 要求所有的bean 的 id 唯一,并且set方法要规范 - byType 要求所有的bean 的 class 唯一,并且set方法要规范 --> <bean id="bookUser" class="zwz.pojo.BookUser" autowire="byName"/>
测试
@Test public void testAutowire(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); BookUser bookUser = (BookUser) context.getBean("bookUser"); System.out.println(bookUser); }
BookUser(book=Book(bookName=自动装配的BookName, pageNum=66)) Process finished with exit code 0
3.8 Java注解开发(推荐)
实体类House
@Data @AllArgsConstructor @NoArgsConstructor public class House { @Value("幸福之家") private String houseName; @Value("99") private String houseSize; }
XML配置
<!--使用注解必须开启注解支持--> <context:annotation-config/> <bean id="house" class="zwz.pojo.House"/>
测试
@Test public void testJava(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); House house= (House) context.getBean("house"); System.out.println(house); }
House(houseName=幸福之家, houseSize=99) Process finished with exit code 0
3.9 spring常用注解
- @Autowired 直接在变量上使用,也可以在set方法上使用,需要符合名字byName原则 【 最常用!!!】
- @Nullable 直接在变量上使用,表示这个变量允许为空
- @Qualifier(value=“xxx”) 直接在变量上使用,指定bean的id
- @Resource 或者 @Resource(value=“xxx”) ,同 @Autowired 和 @Qualifier(value=“xxx”)
- @Value(“xxx”),在实体类的某个变量注入值,也可放在set方法上
- @Component,在spring中注册装配bean,通用
- @Repository,在spring中注册装配bean,一般放在Dao层上
- @Service,在spring中注册装配bean,一般放在Service层上
- @Controller,在spring中注册装配bean,一般放在Controller层上
- @Scope(“prototype/singleton”),配置实体类的单例/原型模式
- @Configuration,Java配置类的注解
四、 AOP 面向切面编程
4.1 静态代理
- 抽象角色:接口/抽象类,代表某种需求【租房】
- 真实角色:被代理的角色【房东】
- 代理角色:代理真实角色【租房中介】
- 客户:访问代理对象【租房的人】
假设场景:XX通信公司在高校内售卖校园卡
【抽象角色】就是卖卡这个需求
// 卖校园卡 public interface SellingCard { void sellingCard(); }
【真实角色】要卖卡的公司
public class Company implements SellingCard{ @Override public void sellingCard() { System.out.println("XX公司要卖校园卡!"); } }
【代理角色】高校内的各级代理,卖卡之余,还提供各种服务
@Data @AllArgsConstructor @NoArgsConstructor public class Proxy implements SellingCard{ private Company company; @Override public void sellingCard() { learnCard(); company.sellingCard(); contract(); collectMoney(); } // 了解校园卡功能 public void learnCard(){ System.out.println("校园卡代理带你了解校园卡费用"); } // 收取卖卡推销费用 public void collectMoney(){ System.out.println("收取卖卡推销费用"); } // 代理指导你签电子合同 public void contract(){ System.out.println("代理指导你签电子合同"); } }
【客户】买卡的学生,访问代理角色,buyCard()为直接找公司购卡,buyCardProxy()为找代理购卡
public class Student { @Test public void buyCardProxy(){ // XX校园卡公司,要卖校园卡 Company company = new Company(); //代理中介,帮公司卖卡,收取一定费用 Proxy proxy = new Proxy(company); // 客户不用面对公司,直接找代理买卡即可 proxy.sellingCard(); } @Test public void buyCard(){ Company company = new Company(); company.sellingCard(); } }
校园卡代理带你了解校园卡费用 XX公司要卖校园卡! 代理指导你签电子合同 收取卖卡推销费用 Process finished with exit code 0
代理模式的优点:
- 让真实角色的需求【房东卖房】更加简单,不用去关注一些其他手续【客户看房,签合同等】
- 其他手续交给了代理,实现业务分工
- 其他手续需求扩展时,方便集中管理
代理模式的缺点:
- 一个真实角色【房东】,就会产生一个代理角色【租房中介】,使得代码量会翻倍
4.2 动态代理
和静态代理一样,也是卖卡这个场景
【需求】卖卡(同静态代理一致)
// 卖校园卡 public interface SellingCard { void sellingCard(); }
【真实角色】要卖卡的公司(同静态代理一致)
public class Company implements SellingCard { @Override public void sellingCard() { System.out.println("XX公司要卖校园卡!"); } }
【代理角色】动态代理
@Data @AllArgsConstructor @NoArgsConstructor public class ProxyAction implements InvocationHandler { // 被代理的接口 private SellingCard sellingCard; public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), sellingCard.getClass().getInterfaces(),this); } // 处理代理实例,并返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { learnCard(); // 动态代理 反射机制实现 Object result = method.invoke(sellingCard, args); collectMoney(); contract(); return result; } // 了解校园卡功能 public void learnCard(){ System.out.println("校园卡代理带你了解校园卡费用"); } // 收取卖卡推销费用 public void collectMoney(){ System.out.println("收取卖卡推销费用"); } // 代理指导你签电子合同 public void contract(){ System.out.println("代理指导你签电子合同"); } }
【客户】买卡的学生
public class Student { @Test public void testProxyAction(){ // 真实角色 Company company = new Company(); // 代理角色 ProxyAction pa = new ProxyAction(); // 通过调用程序处理调用的接口对象 | 就是让代理实现 SellingCard 卖卡的接口 pa.setSellingCard(company); // 获得代理类 | proxy 就是动态生成的 SellingCard proxy = (SellingCard) pa.getProxy(); proxy.sellingCard(); } }