P70 IoC注解之反射注解
P70 start :
Component文件中的 内容:
package com.powernode.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解。
*/
//标注注解的注解,叫做元注解。@Target注解用来修饰@Component可与出现的位置.
//以下表示@Component注解可以出现在类上,属性上。
//@Target(value = {ElementType.TYPE,ElementType.FIELD})
//以下表示@Component注解可以出现在类上
//@Target(value = {ElementType.TYPE})
//使用某个注解的时候,如果注解的属性名是value的话,value可以省略。
//@Target({ElementType.TYPE})
//使用某个注解的时候,如果注解的属性值是数组,并且数组中只有一个元素,大括号可以省略。
@Target(ElementType.TYPE)
//@Retention(RetentionPolicy.SOURCE),表示这个注解只保留在java源文件里面
//@Retention(RetentionPolicy.CLASS),表示java源文件和class文件中都有但是不能被反射机制读取
//@Retention需要被反射的话需要写RUNTIME
//@Retention 也是一个元注解。用来标注@Component注解最终保留在class文件当中,并且可以被反射机制读取。
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
//定义注解的属性
//String是属性类型
//value是属性名
String value();
//其他的属性
//属性类型是String
//属性名是name
//String name();
//数组属性
//属性类型是: String[]
//属性名:names
//String[] names();
//int[] ages();
//int age();
}
User类中的内容:
package com.powernode.bean;
import com.powernode.annotation.Component;
//@Component(属性名 = 属性值, 属性名 = 属性值,属性名 = 属性值...)
//@Component(value="userBean")
//如果属性名是value,value可以省略。
@Component("userBean")
public class User {
//编译器报错,不能出现在这里。
//@Component(value = "test")
// private String name;
}
ReflectAnnotationTest1类中的内容:
package com.powernode.client;
import com.powernode.annotation.Component;
public class ReflectAnnotationTest1 {
public static void main(String[] args) throws Exception{
//通过反射机制怎么读取注解
//获取类
Class<?> aClass = Class.forName("com.powernode.bean.User");
//判断类上面有没有这个注解
if (aClass.isAnnotationPresent(Component.class)) {
//获取类上的注解
Component annotation = aClass.getAnnotation(Component.class);
//访问注解属性
System.out.println(annotation.value());
}
}
}
运行结果:
userBean
P70 END
P71 Spring IoC注解之组件扫描原理
P71 start :
'.'在正则表达式里代表任意字符
Component注解文件中的内容:
package com.powernode.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解。
*/
//标注注解的注解,叫做元注解。@Target注解用来修饰@Component可与出现的位置.
//以下表示@Component注解可以出现在类上,属性上。
//@Target(value = {ElementType.TYPE,ElementType.FIELD})
//以下表示@Component注解可以出现在类上
//@Target(value = {ElementType.TYPE})
//使用某个注解的时候,如果注解的属性名是value的话,value可以省略。
//@Target({ElementType.TYPE})
//使用某个注解的时候,如果注解的属性值是数组,并且数组中只有一个元素,大括号可以省略。
@Target(ElementType.TYPE)
//@Retention(RetentionPolicy.SOURCE),表示这个注解只保留在java源文件里面
//@Retention(RetentionPolicy.CLASS),表示java源文件和class文件中都有但是不能被反射机制读取
//@Retention需要被反射的话需要写RUNTIME
//@Retention 也是一个元注解。用来标注@Component注解最终保留在class文件当中,并且可以被反射机制读取。
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
//定义注解的属性
//String是属性类型
//value是属性名
String value();
//其他的属性
//属性类型是String
//属性名是name
//String name();
//数组属性
//属性类型是: String[]
//属性名:names
//String[] names();
//int[] ages();
//int age();
}
Order类中的内容:
package com.powernode.bean;
import com.powernode.annotation.Component;
//@Component("orderBean")
public class Order {
}
User类中的内容:
package com.powernode.bean;
import com.powernode.annotation.Component;
//@Component(属性名 = 属性值, 属性名 = 属性值,属性名 = 属性值...)
//@Component(value="userBean")
//如果属性名是value,value可以省略。
//@Component("userBean")
public class User {
//编译器报错,不能出现在这里。
//@Component(value = "test")
// private String name;
}
Vip类中的内容:
package com.powernode.bean;
import com.powernode.annotation.Component;
@Component("vipBean")
public class Vip {
}
ComponentScan类中的内容:
package com.powernode.client;
import com.powernode.annotation.Component;
import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class ComponentScan {
public static void main(String[] args){
Map<String,Object> beanMap = new HashMap<>();
//目前只知道一个包的名字,扫描这个包下所有的类,当这个类上有@Component注解的时候,实例化该对象,然后放到Map集合中。
String packageName = "com.powernode.bean";
//开始写扫描程序
// . 这个正则表达式代表任意字符。这里的"."必须是一个普通的“.”字符。不能是正则表达式中的“.”
//在正则表达式当中怎么表示一个普通的“.”字符呢?使用 \. 正则表达式代表一个普通的 . 字符。
String packagePath = packageName.replaceAll("\\.", "/");
//System.out.println(packagePath);
// com是在类的根路径下的一个目录。
URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
String path = url.getPath();
//System.out.println(path);
//获取一个绝对路径下的所有文件
File file = new File(path);
File[] files = file.listFiles();
Arrays.stream(files).forEach(f ->{
try{
//System.out.println(f.getName());
//System.out.println(f.getName().split("\\.")[0]);
String className = packageName + "." + f.getName().split("\\.")[0];
//System.out.println(className);
//通过反射机制解析注解
Class<?> aClass = Class.forName(className);
//判断类上是否有这个注解
if (aClass.isAnnotationPresent(Component.class)) {
//获取注解
Component annotation = aClass.getAnnotation(Component.class);
String id = annotation.value();
// 有这个注解的都要创建对象
Object obj = aClass.newInstance();
beanMap.put(id,obj);
}
}catch(Exception e){
e.printStackTrace();
}
});
System.out.println(beanMap);
}
}
运行结果:
{vipBean=com.powernode.bean.Vip@685f4c2e}
P71 END
P72 IoC注解之声明Bean的注解
P72 start:
12.2 声明Bean的注解
负责声明Bean的注解,常见的包括四个:
- @Component
- @Controller
- @Service
- @Repository
其余三个注解是@Component注解的别名
建议持久层用@Repository(dao)
业务层用@service
表示层建议用@Controller(web)
P72 end
P73 IoC注解之Spring注解的使用
P73 start :
12.3 Spring注解的使用
如何使用以上的注解呢?
- 第一步:假如aop的依赖
- 第二步:在配置文件中添加context命名空间
- 第三步:在配置文件中指定扫描的包
- 第四步:在Bean类上使用注解
(注解不写名字,默认value=" "时,默认为类名首字母小写)
Order类中的内容:
package com.powernode.srping6.bean;
import org.springframework.stereotype.Service;
@Service //如果你把整个value属性全部省略了,bean有没有默认的名称? 有:类名首字母变小写就是bean的名字。
public class Order {
}
/**
* 以上的这个注解@Service就相当于以下的这个配置信息:
* <bean id = "order" class = "com.powernode.spring6.bean.Order"></bean>
*/
OrderService类中的内容:
package com.powernode.srping6.bean;
import org.springframework.stereotype.Service;
//@Service(value = "os")
//@Service("os")
@Service //bean的名字是:orderService
public class OrderService {
}
Student类中的内容:
package com.powernode.srping6.bean;
import org.springframework.stereotype.Repository;
@Repository
public class Student {
}
User类中的内容:
package com.powernode.srping6.bean;
import org.springframework.stereotype.Component;
@Component("userBean")
public class User {
}
Vip类中的内容:
package com.powernode.srping6.bean;
import org.springframework.stereotype.Controller;
@Controller("vipBean")//如果属性名是value的haul, 这个属性名可以省略。
public class Vip {
}
/**
* 以上的这个注解@Controller("vipBean")就相当于以下的这个配置信息:
* <bean id = "vipBean" class = "com.powernode.spring6.bean.Vip"></bean>
*/
spring配置文件spring.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: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">
<!--给Spring框架指定要扫描哪些包中的类 -->
<context:component-scan base-package="com.powernode.srping6.bean" />
</beans>
测试类中的内容:
package com.powernode.spring6.test;
import com.powernode.srping6.bean.*;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class IoCAnnotationTest {
@Test
public void testBeanComponent(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
User userBean = applicationContext.getBean("userBean", User.class);
System.out.println(userBean);
Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vipBean);
Order orderBean = applicationContext.getBean("order", Order.class);
System.out.println(orderBean);
Student studentBean = applicationContext.getBean("student", Student.class);
System.out.println(studentBean);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
System.out.println(orderService);
}
}
运行结果:
com.powernode.srping6.bean.User@60957c0f
com.powernode.srping6.bean.Vip@293a5f75
com.powernode.srping6.bean.Order@fcb4004
com.powernode.srping6.bean.Student@1dd6d4b7
com.powernode.srping6.bean.OrderService@56e8b606
P73 end
P74 IoC注解之解决多个包扫描问题
P74 start :
如果有多个包怎么办?有两种解决方案:
- 第一种:在配置文件中指定多个包,用逗号隔开。
- 第二种:指定多个包的共同父包。
上集中的 内容为此集内容一起的:
另外一个包中的文件 OrderDao类中的内容:
package com.powernode.spring6.dao;
import org.springframework.stereotype.Component;
@Component
public class OrderDao {
}
spring配置文件spring.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: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">
<!--给Spring框架指定要扫描哪些包中的类 -->
<!-- <context:component-scan base-package="com.powernode.srping6.bean" />-->
<!--多个包,使用逗号隔开。 -->
<!-- <context:component-scan base-package="com.powernode.spring6.bean,com.powernode.spring6.dao" />-->
<!--多个包,也可以指定这多个包共同的父包,但是这肯定要牺牲一部分效率 -->
<context:component-scan base-package="com.powernode.spring6" />
</beans>
测试类中的内容:
package com.powernode.spring6.test;
import com.powernode.spring6.bean.*;
import com.powernode.spring6.dao.OrderDao;
import com.powernode.spring6.bean.*;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class IoCAnnotationTest {
@Test
public void testBeanComponent(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
User userBean = applicationContext.getBean("userBean", User.class);
System.out.println(userBean);
Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vipBean);
Order orderBean = applicationContext.getBean("order", Order.class);
System.out.println(orderBean);
Student studentBean = applicationContext.getBean("student", Student.class);
System.out.println(studentBean);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
System.out.println(orderService);
OrderDao orderDao = applicationContext.getBean("orderDao", OrderDao.class);
System.out.println(orderDao);
}
}
运行结果:
com.powernode.spring6.bean.User@5cdd09b1
com.powernode.spring6.bean.Vip@8c11eee
com.powernode.spring6.bean.Order@7e8dcdaa
com.powernode.spring6.bean.Student@681a8b4e
com.powernode.spring6.bean.OrderService@5cbe877d
com.powernode.spring6.dao.OrderDao@5c08c46a
P74 END
P75 spring IoC注解之选择性实例化Bean
P75 start :
12.4 选择性实例化Bean
假设在某个包下有很多Bean,有的Bean上标注了Component,有的标注了Controller,有的标注了Service,有的标注了Repository,现在由于某种特殊业务的需要,只允许其中所有的Controller参与Bean管理,其它的都不实例化,这应该怎么办呢?
另外一个包:
A类中的内容:
package com.powernode.spring6.bean2;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
@Component
public class A {
public A() {
System.out.println("A的无参数构造方法执行");
}
}
@Controller
class B{
public B() {
System.out.println("B的无参数构造方法执行");
}
}
@Service
class C{
public C() {
System.out.println("C的无参数构造方法执行");
}
}
@Repository
class D{
public D() {
System.out.println("D的无参数构造方法执行");
}
}
@Controller
class E{
public E() {
System.out.println("E的无参数构造方法执行");
}
}
spring配置文件spring.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: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">
<!--
第一种解决方案:
use-default-filters="false"
如果这个属性是false,表示com.powernode.spring6.bean2包下所有的带有声明Bean的注解全部失效。@Component @Controller @Service @Repository全部失效
-->
<!-- <context:component-scan base-package="com.powernode.spring6.bean2" use-default-filters="false">-->
<!--想让谁生效就包含谁 -->
<!--只有@Repository @Service 被包含进来,生效 -->
<!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
<!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>-->
<!-- </context:component-scan>-->
<!--
第二种解决方案:
use-default-filters="true"
如果这个属性的值是true,表示com.powernode.spring6.bean2下的所有的带有声明Bean的注解全部生效。
use-default-filters="true" 默认值就是true,不用写。
-->
<context:component-scan base-package="com.powernode.spring6.bean2" use-default-filters="true">
<!--@Controller注解失效 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
测试类中的内容为:
@Test
public void testChoose(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-choose.xml");
}
运行结果:
A的无参数构造方法执行
C的无参数构造方法执行
D的无参数构造方法执行
P75 end
P76 Spring IoC注解之@Value注解
P76 start :
12.5负责注入的注解
@Component @Controller @Service @Repository 这四个注解是用来声明Bean的,声明后这些Bean将被实例化。接下来我们看一下,如何给Bean的属性赋值。给Bean属性赋值需要用到这些注解:
- @Value
- @Autowired
- @Qualifier
- @Resource
12.5.1 @Value
当属性的类型是简单类型时,可以使用@Value注解进行注入。
@Value注解也可以用在方法上
@Value注解还可以用在构造方法上
@Value只负责注入简单类型
@Value可以直接用在属性上,可以用在set方法上,可以用在构造方法的形参上
(@Value不需要set方法也可以注入)
P76 end
P77 Spring IoC注解之@Value注解
P77 start :
12.5.2 @Autowired与@Qualifier
@Autowired注解可以用来注入非简单类型。被翻译为:自动连线的,或者自动装配。
单独使用@Autowired注解,默认根据类型装配。【默认是byType】
看一下它的源码:
@Target({ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.PARAMETER,ElementType.FIELD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired{
boolean required() default true;
}
源码中有两处需要注意:
- 第一处:该注解可以标注在哪里?
- 构造方法上
- 方法上
- 形参上
- 属性上
- 注解上
- 第二处:该注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须是存在的,如果不存在则报错。如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错。
OrderDao接口中的内容:
package org.powernode.dao;
public interface OrderDao {
void insert();
}
OrderDaoImplForMySQL类中的内容:
package org.powernode.dao.impl;
import org.powernode.dao.OrderDao;
import org.springframework.stereotype.Repository;
@Repository("orderDaoImplForMySQL")
public class OrderDaoImplForMySQL implements OrderDao {
@Override
public void insert() {
System.out.println("MySQL数据库正在保存订单信息...");
}
}
OrderDaoImplForOracle类中的内容:
package org.powernode.dao.impl;
import org.powernode.dao.OrderDao;
import org.springframework.stereotype.Repository;
@Repository("orderDaoImplForOracle")
public class OrderDaoImplForOracle implements OrderDao {
@Override
public void insert() {
System.out.println("Oracle数据库正在保存订单信息...");
}
}
spring配置文件spring-autowired.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: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="org.powernode" />
</beans>
OrderService类中的内容:
package org.powernode.service;
import org.powernode.dao.OrderDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Service("orderService")
public class OrderService {
//@Autowired 注解使用的时候,不需要指定任何属性,直接使用这个注解即可。
//这个注解的作用是根据类型byType进行自动装配。
// @Autowired
//private OrderDao orderDao;
//如果想解决以上问题,只能根据名字进行装配。
//@Autowired和@Qualifier联合使用,可以根据名字进行装配。
@Autowired
//@Qualifier("orderDaoImplForOracle")
@Qualifier("orderDaoImplForMySQL")
private OrderDao orderDao;
public void generate(){
orderDao.insert();
}
}
测试类中的内容:
@Test
public void testAutowired(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowired.xml");
org.powernode.service.OrderService orderService = applicationContext.getBean("orderService", org.powernode.service.OrderService.class);
orderService.generate();
}
运行结果:
MySQL数据库正在保存订单信息...
P77 end
P78 Spring IoC注解之@Autowired注解可以出现的位置
P78 start :
Time:4h+
视频网址:
https://www.bilibili.com/video/BV1Ft4y1g7Fb?p=77&vd_source=ff898217e3612fcbdefecfdc49657ab1