注解创建Bean
我们以前创建一个对象是采用XML配置的
<bean id="customer" class="com.lff.domain.Customer" />
采用注解的方式创建,我们首先需要在XML配置文件配置需要扫描的包,能够扫描到所有的@Component注解
<Context:component-scan base-package="com.lff.test" />
如果整个项目都没有XML文件,使用@ComponentScan 注解替代
在需要使用注解的类前面加@Component
@Component
public class Person {
}
外界使用
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) ctx.getBean("person");
System.out.println(person);
获取Bean时,默认使用类名的首字母小写的形式做为id当然你也可以使用注解的value属性设置Bean的id。
@Component(value = "person1")
public class Person {
}
外界获取时,使用person1做为id。
Person person = (Person) ctx.getBean("person1");
在项目中我们需要分层,比如Serlet层、Service层、Dao层。我们对不同的层如果都使用@Component当然没有问题,但不够语义化。
Servlet层我们通常使用@Controller
@Controller
public class LoginServlet {
}
Service层使用@Service注解
@Service(value = "loginService")
public class LoginServiceImpl {
}
Dao层使用@Repository注解
@Repository("loginDao")
public class LoginDaoImpl {
}
当然上面@Controller、@Service、@Repository和@Component功能上并没有区别,都是为了把对象放在IOC容器里面。
一个比较全的示例
@Component(value = "person1")
@Scope("singleton") //单例,如果不想为单例值为prototype
@Lazy //表示Scope为singleton延时加载
@ComponentScan("com.lff.test") //扫描com.lff.test里面的包
//@ComponentScans({
// @ComponentScan("com.lff.test"),
// @ComponentScan("com.lff.send")
//})
public class Person {
}
注解实现注入
当注入的类型为Bean时,使用@Autowired注解
@Component(value = "person")
public class Person {
private Dog dog;
@Autowired
public void setDog(Dog dog) {
this.dog = dog;
}
}
@Autowired默认是根据参数的类型进行注入,看参数类型是Dog类型的然后从IOC容器里面找是否有Dog类型的对象。
以下的@Autowired写法都正确
直接放在成员变量上面
@Component(value = "person")
public class Person {
@Autowired
private Dog dog;
public void setDog(Dog dog) {
this.dog = dog;
}
}
直接通过反射去找
@Component(value = "person")
public class Person {
@Autowired
private Dog dog;
}
也可以使用@Qualifier注解根据id来查找
@Component(value = "person")
public class Person {
private Dog dog;
@Autowired(required = false) //false找不到对应的Bean不会抛出异常,默认为true会抛出异常
@Qualifier("dog") //根据id来找
public void setDog(Dog dog) {
this.dog = dog;
}
}
当注入的类型为基本类型(String、BigDecimal)时,使用@Value注解
@Component(value = "person")
public class Person {
private String name;
@Value("Cliff")
public void setName(String name) {
this.name = name;
}
}
name的名称可以从配置文件里面读取。
@Component(value = "person")
@PropertySource("person.properties") //加载配置文件
//@PropertySources({
// @PropertySource("配置文件1的名称"),
// @PropertySource("配置文件2的名称"),
// @PropertySource("配置文件3的名称"),
//})
public class Person {
private String name;
@Value("${name}") //从配置文件读取name
public void setName(String name) {
this.name = name;
}
}
注解实现AOP
首先需要在XML文件配置下面的代码代替之前的
<aop:aspectj-autoproxy />
上面的配置其实也可以使用@EnableAspectJAutoProxy注解替代
新建切面类,可以把要拦截的功能都写这个类里面,只需在里面增加方法即可。
@Aspect //标识一个切面类
@Component //这个是要加入到IOC容器里面
@EnableAspectJAutoProxy
public class PersonAop {
@Around("within(com.lff.test..*)") //设置切入点,要求被注解的方法要有返回值,
public Object log(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //方法必须传入一个ProceedingJoinPoint类型的参数,要求的
System.out.println("before---------");
//调用目标方法
Object object = proceedingJoinPoint.proceed();
System.out.println("after---------");
return object;
}
}
一般@EnableAspectJAutoProxy注解可以写到@Configuration注解的类中,关于@Configuration注解我们后面会讲到。
外界使用
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) ctx.getBean("person");
person.run("hahaha");
打印日志
before---------
hahaha
after---------
也可以把切入点的代码使用空方法抽取出来
@Aspect //标识一个切面类
@Component //这个是要加入到IOC容器里面
public class PersonAop {
@Pointcut("within(com.lff.test..*)")
public void pointCut(){} //抽取切入点
@Around("pointCut()") //这里直接调用方法即可
public Object log(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //方法必须传入一个ProceedingJoinPoint类型的参数,要求的
System.out.println("before---------");
//调用目标方法
Object object = proceedingJoinPoint.proceed();
System.out.println("after---------");
return object;
}
}
注解替换XML文件
使用@Configuration的注解用于替换applicationContext.xml文件
@Configuration
public class AppConfig {
}
此时AppConfig的类对象会自动放在IOC容器里面。因为@Configuration也是一个@Component注解(用之前也要被扫描到)。
可以在@Configuration修饰的类中使用@Bean注解修饰方法,创建Bean对象。
@Configuration
public class AppConfig {
@Bean
// @Bean("person") //设置获取时的id
@Scope("prototype")
@Lazy
public Person getPerson(){
return new Person();
}
}
默认情况下,方法名就是Bean的id,可以通过 @Bean(“person”)的方式设置id。
被@Bean修饰的方法,他的参数也会被自动注入(当然前提是参数的类型能够在IOC容器里面找到)
当@Bean方法的返回值是一个FactoryBean时,会通过调用FactoryBean的getObject返回生产的对象
public class DogFactoryBean implements FactoryBean<Dog> {
@Override
public Dog getObject() throws Exception {
return new Dog();
}
@Override
public Class<?> getObjectType() {
return Dog.class;
}
}
@Configuration
public class AppConfig {
@Bean
public DogFactoryBean dog1(){
return new DogFactoryBean();
}
}
外界使用
Dog dog = (Dog) ctx.getBean("dog1");
创建工厂也使用注解
以前我们使用的是XML,创建一个叫applicationContext.xml的文件,然后我们使用如下代码获取工程
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
当然如果XML文件里面的内容过多,我们也可以新建多个XML配置,只需在在applicationContext.xml文件里面导入其他XML文件即可
<import resource="xxx.xml" />
我们能不能不使用XML的方式去创建工程呢?当然可以
使用注解创建工程的方式有很多种:我们只需要关注某个类有没有被扫描到、有没有被导入进来、有没有放到IOC容器里面就可以了,下面是一个具体的示例
@Configuration
@ComponentScan("com.lff") //扫描包
//@ImportResource("applicationContext.xml") //导入XML,注解和XML混合使用时
public class AppConfig {
@Bean
public DogFactoryBean dog1(){
return new DogFactoryBean();
}
}
外界使用
//ApplicationContext ctx = new AnnotationConfigApplicationContext("com.lff"); //传一个包
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); //传一个包
Dog dog = (Dog) ctx.getBean("dog1");
System.out.println(dog);
使用AnnotationConfigApplicationContext可以传入一个包名,也可以传入一个类的入口类名获取工厂对象。
Bean的创建方式
Bean创建方式总结起来有3种
- @Component 这种方式一般适用于开发者自己写的类,创建方式比较简单比如直接new
@Bean 这种方式适用于创建方式比较复杂的场景,比如别人写好的类我们直接调用创建Bean
@Bean
public xxx dog1(){
调用其他库的代码
return xxx;
}
直接在XML配置Bean的方式,这种方式也最为通用,什么类型的对象都能够创建
因为创建Bean有三种方式,当这三种方式同时存并且id也都相同,默认从IOC容器里面取对象的时候是以哪一种方式创建的呢?
从XML里面创建
证明:
@Component
public class Person {
private String name = "@Component创建";
public void setName(String name) {
this.name = name;
}
}
XML配置
<bean id="person" class="com.lff.test.Person">
<property name="name" value="XML中的Bean" />
</bean>
@Bean方式
@Configuration
@ComponentScan("com.lff") //扫描包
@ImportResource("applicationContext.xml") //导入XML,注解和XML混合使用时
public class AppConfig {
@Bean
public Person person(){
Person person = new Person();
person.setName("这是使用@Bean方式创建的");
return person;
}
}
外界获取
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); //传一个包
Person person = (Person)ctx.getBean("person");
发现person的name是XML中的Bean,可以证明是先从XML配置创建Bean,把XML注释后发现name是“这是使用@Bean方式创建的”说明其次是从@Bean方式创建的