IoC(中)(注解管理、反射)
基于注解管理bean
注解是 @注解名(属性)的格式,是Java中的一种特殊标记
Spring通过注解实现自动装配的方式:
引入依赖-开启组件扫描-使用注解定义Bean-依赖注入
开启组件扫描
Spring默认不支持使用注解进行自动装配,所以要进行配置,开启组件扫描是为了让java扫描注解并实现相应功能。
在bean.xml文件下的beans标签中增加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">
在beans标签下配置context
<context:component-scan base-package="com.qinghe"></context:component-scan>
也有扫描排除和仅扫描某些注解的配置方式,这里不做介绍。
使用注解定义Bean
下面四种注解都可以用来将类标注为SpringBean,其功能一致,只是使用位置不同。
@Component 可以使用在所有层中,但一般用于Java Bean
@Repository 一般使用在Dao层
@Service 通常用于Service层
@Controller 通常用于Controller层
演示如下:
@Component(value = "user") //相当于<bean id=""user" class="...">
若value值不写,默认生成类名首字母小写的value
属性注入
在属性名上添加@Autowired注解,完成对属性的依赖注入,这是根据属性的类型自动找到IoC容器中的Bean进行注入的(由于类前的注解,类对象已经被添加进IoC容器中了)。
示例:
//用来向IoC容器中添加UserController的对象
@Controller
public class UserController {
//自动在容器中找到UserServcie类型的对象并进行注入
//在UserService中也类似进行注解
@Autowired
private UserService userService;
public void add() {
System.out.println("Do Controller.............");
userService.add();
}
}
set注入、构造注入、形参注入
将@Autowired添加到属性对应的set方法与构造器上,也可以实现对应属性的注入。
private UserDao userDao;
//set方法上的注解
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
private UserDao userDao;
//构造器上的注解
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
甚至@Autowired还可以用到形参中,也可以实现依赖注入:
private UserDao userDao;
//在形参前使用
public UserServiceImpl(@Autowired UserDao userDao) {
this.userDao = userDao;
}
注意:若类中只有一个有参构造函数时,@Autowired可以省略
根据名称进行注入
当接口有多个实现类时,仅仅有@Autowired注解就无法实现依赖注入了(不知道该注入哪个实现类)
这个时候就会用到@Qualifier注解,Qualifier的value值为要注入的对象的类名的首字母小写。
@Autowired
@Qualifier(value = "userRedisDaoImpl")
private UserDao userDao;
@Repository
public class UserRedisDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("Do Redis......................");
}
}
@Resource注入
配置依赖
<!-- @Resouce注解的依赖配置-->
<dependencies>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
@Resource注解是JDK拓展包的一部分,除了JDK8到JDK11的版本,其他版本都需要添加依赖才可以使用
@Resource注解使用在属性名或者set方法上,其默认通过名称进行注入,会寻找和@Resource(name = “xxx”)中name相同的类上的注解,若没有写name属性的话,会默认按照需要注入的属性名进行寻找,若仍然找不到,则会按照类型寻找。
//该value与测试文件中的getBean()对应
@Controller(value = "myUserController")
public class UserController {
@Resource
private UserService userService;
//该value与UserController中的@Resource对应,其没有生命name值,默认为userService,与该类对应
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Resource(name = "myUserDao")
private UserDao userDao;
//该value与UserServiceImpl的@Resource对应,其value值等于Resource中的name值
@Repository(value = "myUserDao")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("Do Dao...............");
}
}
全注解开发
使用配置类代替配置文件实现全注解开发
创建配置类:
@Configuration //配置类的声明
@ComponentScan("com.qinghe.spring6") //开启组件扫描
public class SpringConfig {
}
在测试中创建的IoC容器有所改变:
public class TestUserControllerAnno {
public static void main(String[] args) {
//创建不同的实现类并传入配置文件的类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserController myUserController = context.getBean("myUserController", UserController.class);
myUserController.add();
}
}
反射回顾
反射就是通过在运行时的类,可以获取他的类名、属性和方法并加以使用的技术。
通过反射获取类名并创建对象:
//获取对象的多种方式
@Test
public void test01() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//类名.class
Class clazz1 = Car.class;
//对象.getClass()
Class clazz2 = new Car().getClass();
//Class.forName("全路径")
Class clazz3 = Class.forName("com.qinghe.reflect.Car");
//创建对象
Car car = (Car) clazz3.getDeclaredConstructor().newInstance();
System.out.println(car);
}
通过获取构造器的方式创建对象
@Test
public void test02() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class clazz = Car.class;
//获取clazz类的所有构造器
//直接get的方法无法获取私有的方法或构造器
Constructor[] constructor = clazz.getConstructors();
//若要获取私有的方法,则需要使用getDeclaredConstructor()
Constructor[] constructor1 = clazz.getConstructors();
//遍历一遍得到的构造器
for(Constructor c : constructor1) {
System.out.println("方法名称:" + c.getName() + " 参数个数:" + c.getParameterCount()); //输出构造器的名字
}
//使用获取到的构造器创建对象
// Constructor c = clazz.getConstructor(String.class, int.class, String.class);
// Car car = (Car)c.newInstance("宾利", 50, "黑色");
// System.out.println(car);
//若构造器为私有,使用getDeclaredConstructor()获取
Constructor c1 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
c1.setAccessible(true); //令私有允许访问
c1.newInstance("路虎", 90, "帝王金");
System.out.println(c1);
}
反射获取属性与设置属性的方法
@Test
public void test03() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获取字节码文件
Class clazz = Car.class;
//获取构造器,创建对象
Car car = (Car)clazz.getDeclaredConstructor().newInstance();
//获取所有的属性,并存储在一个数组中
Field[] fields = clazz.getDeclaredFields();
//遍历所有属性
for (Field field : fields) {
//设置name属性
if(field.getName().equals("name")) {
//令私有属性可被修改
field.setAccessible(true);
//设置属性,对象与属性内容
field.set(car, "宝马");
}
System.out.println(field.getName());
}
System.out.println(car);
}
反射获取方法的方法
@Test
public void test04() throws InvocationTargetException, IllegalAccessException {
Car car = new Car("劳斯莱斯", 900000, "彩虹色");
Class clazz = car.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
if (method.getName().equals("toString")) {
method.setAccessible(true);
//.incoke()的第一个参数要传对象,若该方法还需求参数,则要在后面写跟上
String invoke = (String)method.invoke(car);
System.out.println("toString方法执行了:" + car);
}
}
}
注意.getDeclaredMethods()可以获取类中所有定义的方法(包括私有)
.getMethods()会获取所有public方法(无法获取私有),同时也会获取父类中的所有方法