架构
?UserMapper
public interface UserMapper {
@Select("select 'user'")
String selectById();
}
UserService
@Component
public class UserService {
@Autowired
private UserMapper userMapper;
public void test(){
System.out.println(userMapper.selectById());
}
}
?主类
@ComponentScan("com.luban") //配置扫描路径 注册bean
public class LubanApplication {
public static void main(String[] args) {
//前三行代码启动spring
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //获取spring容器
applicationContext.register(LubanApplication.class);//配置spring
applicationContext.refresh();
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.test();
}
}
mybatis.xml?
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<!--使用jdbc事务管理-->
<transactionManager type="JDBC"/>
<!--使用连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/luban?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123123"/>
</dataSource>
</environment>
</environments>
</configuration>
启动:
发现报错:
意思是spring容器中找不到UserService来进行属性注入?
当扫描到UserService上的注解@Compoent,会去实例化UserService,然后因为属性上存在注解@Autowired ,因此spring去帮我们进行属性注入,但是spring容器中并没有UserMapper类型的实例,因此报错!!!
但如果我们修改注解为@Autowired(required = false)
?再启动主类:
发现不会报刚刚的错误了,因为require=false,spring找不到就算了,而默认的true,一定要对属性赋值,因此报错
我们可以通过FactoryBean来产生一个UserMapper的代理对象,把他注入bean容器中,这样spring在近些属性注入的时候就不会报错了!!!(这个就是mybatis产生mapper对象的原理)
先创建一个User类
public class User {
}
主类中编程式声明
@ComponentScan("com.luban") //配置扫描路径 注册bean
public class LubanApplication {
public static void main(String[] args) {
//前三行代码启动spring
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //获取spring容器
applicationContext.register(LubanApplication.class);//配置spring
//编程式
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(LubanFactoryBean.class);
applicationContext.registerBeanDefinition("user",beanDefinition);
applicationContext.refresh();
System.out.println(applicationContext.getBean("user"));
/*UserService userService = applicationContext.getBean("userService", UserService.class);
userService.test();*/
}
}
输出:
?
那我们直接将这里换成UserMapper吗,那可不行,spring是通过推断构造器来实现实例化的,接口可没构造器,因此你换成UserMapper会报错。?
别急,总会有方法的
新加一个类实现FactoryBean接口,这个类是个特殊的Bean
public class LubanFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
Person person = new Person();
return person;
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
}
修改
输出:
?再输出:
?说明一下这个特殊的Bean,若你取名为xxx,那么外面的类对应的Bean名字就是&xxx
我们可以将UserMapper的代理对象的创建在这个类中完成,这样不就可以完成属性注入了吗?
?放代码
public class LubanFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
//mybatis UserMapper的代理对象
Object instance = Proxy.newProxyInstance(LubanFactoryBean.class.getClassLoader(), new Class[]{UserMapper.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName());
return null;
}
});
return instance;
}
@Override
public Class<?> getObjectType() {
return UserMapper.class;
}
}
ComponentScan("com.luban") //配置扫描路径 注册bean
public class LubanApplication {
public static void main(String[] args) {
//前三行代码启动spring
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //获取spring容器
applicationContext.register(LubanApplication.class);//配置spring
//编程式
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(LubanFactoryBean.class);
applicationContext.registerBeanDefinition("xxx",beanDefinition);
applicationContext.refresh();
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.test();
}
}
输出:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
此jar里面就是我们的LubanFactoryBean类,因此可以通过注解@Autowired,直接拿来用。但是jar里的类还是和此自定义类有所不同的。我们的LubanFactoryBean是写死了UserMapper的。那jar里肯定是不能写死的
新增接口
public interface OrderMapper {
@Select("select 'user'")
String selectById();
}
UserService
@Component
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private OrderMapper orderMapper;
public void test(){
System.out.println(userMapper.selectById());
System.out.println(orderMapper.selectById());
}
}
启动报错
?那我们不可能再自定义一个LubanOrderFactoryBean(太愚蠢了)
因此我们修改LubanFactoryBean
public class LubanFactoryBean implements FactoryBean {
private Class mapperClass;
public LubanFactoryBean(Class mapperClass) {
this.mapperClass = mapperClass;
}
@Override
public Object getObject() throws Exception {
//mybatis UserMapper的代理对象
Object instance = Proxy.newProxyInstance(LubanFactoryBean.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//输出toString
System.out.println(method.getName());
return null;
}
});
return instance;
}
@Override
public Class<?> getObjectType() {
return mapperClass;
}
}
主类
@ComponentScan("com.luban") //配置扫描路径 注册bean
public class LubanApplication {
public static void main(String[] args) {
//前三行代码启动spring
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //获取spring容器
applicationContext.register(LubanApplication.class);//配置spring
//编程式
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(LubanFactoryBean.class); //UserMapper的代理对象
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
applicationContext.registerBeanDefinition("xxx",beanDefinition);
AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition1.setBeanClass(LubanFactoryBean.class); //OrderMapper的代理对象
beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(OrderMapper.class);
applicationContext.registerBeanDefinition("xxx1",beanDefinition1);
applicationContext.refresh();
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.test();
}
}
输出:
属实还是有点麻烦,不可能你多一个接口,我就要声明一个beanDefintion吧。
我们自定义一个类实现
public class LubanImportBeanDefinitionRegistarar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
//编程式
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(LubanFactoryBean.class); //UserMapper的代理对象
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
registry.registerBeanDefinition("xxx",beanDefinition);
AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition1.setBeanClass(LubanFactoryBean.class); //OrderMapper的代理对象
beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(OrderMapper.class);
registry.registerBeanDefinition("xxx1",beanDefinition1);
}
}
同时简化主类
@ComponentScan("com.luban") //配置扫描路径 注册bean
@Import(LubanImportBeanDefinitionRegistarar.class)
public class LubanApplication {
public static void main(String[] args) {
//前三行代码启动spring
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //获取spring容器
applicationContext.register(LubanApplication.class);//配置spring
applicationContext.refresh();
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.test();
}
}
主类多了个注解@Import(LubanImportBeanDefinitionRegistarar.class),作用是去执行LubanImportBeanDefinitionRegistarar.class里的方法!!!
启动发现Ok!!!LubanImportBeanDefinitionRegistarar也是jar中的一个类
现在的问题是,LubanImportBeanDefinitionRegistarar类中的东西也是写死的!!!?、
我们应该去扫描
新建一个扫描类
public class LubanMapperScanner extends ClassPathBeanDefinitionScanner {
public LubanMapperScanner(BeanDefinitionRegistry registry) {
super(registry);
}
}
执行扫描
?
?scan等于0说明没扫描到,原因是他只会去扫描类,不会去扫描接口,因此返回0;
那我们要让他扫描接口,可以这样做
扫描类中添加此方法(修改去扫描接口而不是类)
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface();
}
?同时此类新增
?再次debug
扫描类中重写方法
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
return beanDefinitionHolders;
}
再次修改扫描包中的内容
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
beanDefinition.setBeanClassName(LubanFactoryBean.class.getName());
}
return beanDefinitionHolders;
}
?删掉这个
启动输出:
修改LubanFactoryBean
public class LubanFactoryBean implements FactoryBean {
private Class mapperClass;
private SqlSession sqlSession;
@Autowired
public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
sqlSessionFactory.getConfiguration().addMapper(mapperClass);
this.sqlSession = sqlSessionFactory.openSession();
}
public LubanFactoryBean(Class mapperClass) {
this.mapperClass = mapperClass;
}
@Override
public Object getObject() throws Exception {
//mybatis UserMapper的代理对象
return sqlSession.getMapper(mapperClass);
}
@Override
public Class<?> getObjectType() {
return mapperClass;
}
}
主类注册Bean
?这样也可以照常输出。mybatis的jar大致就是这样实现的
|