◎筱米加步枪◎.Blog

Happy coding

Sping中自定义默认注解名生成器

在类中贴上@Repository等标签,相当于在Spring容器里定义了一个Bean类,这个Bean的名称如果不指定名称,将默认使用当前类名(不含包名)的首字母小写作为Bean的名字.

需求:

要确保这个Bean名称要唯一,显然这样定义的Bean名称太过简单,比较容易造成一样情况,如果能使用全类名作为Bean可防止这样的情况,因为不存在全类名一样的类.例如:org.kirinsoft.frame.AClass

技术实现:

指定定义一个Bean名字生成器,实现Spring提供的BeanNameGenerator,在generateBeanName方法中设计出自己需要的Bean名称.

另外在Spring配置文件中的自动注解配置要指定名字生成器类,如:

	<!-- 自动注解配置 -->
	<context:annotation-config />
	<context:component-scan base-package="com.kirinsoft.frame"
		name-generator="com.kirinsoft.frame.pub.UniqueBeanNameGenerator" />

具体实现方法:

/**
 * 唯一Bean名称生成器.以类全名作为Bean名称.
 */
public class UniqueBeanNameGenerator implements BeanNameGenerator{

	@Override
	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		return definition.getBeanClassName();
	}
}

 

Spring中事务的作用范围

下面一种情景:

在做项目中通常会进行分层架构.假设可分为Service、Biz、Dao3层.关系如下:

一个Service类调用多个Biz类,一个Biz调用多个Dao类完成业务逻辑.

基于这种情况,通常我们把事务控制到Service层.

很当然的引入Spring中配置:(其他配置略)

<property name="beanNames">
            <list>
                <value>*ServiceImpl</value>
            </list>
        </property>

 

那么,引入一个问题:如果同时对Service层和Biz层加入Spring的事务配置中,是哪一层起作用呢、?

换句话说,假设一个Service有两个Biz类,那么第一个Biz类操作成功,而第二个操作失败,此时第一个Biz是否有回滚呢。?

对应的Spring配置:

        <property name="beanNames">
            <list>
                <value>*ServiceImpl</value>
                <value>*BizImpl</value>
            </list>
        </property>

通过实验证明可得到结论:哪个包含的范围大,哪个事务起作用。

上面的假设,Service的事务起作用,第一个Biz会被回滚掉。

Spring事务属性说明

Spring事务属性配置属性说明如下:

PROPAGATION_REQUIRED

【支持当前事务,如果当前没有事务,就新建一个事务。(常用)】


PROPAGATION_SUPPORTS

【支持当前事务,如果当前没有事务,就以非事务方式执行】


PROPAGATION_MANDATORY

【支持当前事务,如果当前没有事务,就抛出异常】


PROPAGATION_REQUIRES_NEW

【新建事务,如果当前存在事务,把当前事务挂起】


PROPAGATION_NOT_SUPPORTED

【以非事务方式执行操作,如果当前存在事务,就把当前事务挂起】


PROPAGATION_NEVER   

【以非事务方式执行,如果当前存在事务,则抛出异常】


PROPAGATION_NESTED 

【如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作】


-Exception表示有Exception抛出时,事务回滚。-代表回滚+就代表提交

readonly 就是read only, 设置操作权限为只读,一般用于查询的方法,优化作用

Spring AOP 之 RegexpMethodPointcutAdvisor

昨天,做了有关日志的AOP,对相关的AOP知识总结如下:

1.引入AOP(Aspect Oroented Programming) 面向切面编程,是消除代码重复的一种方法。

2.Spring AOP 中提供了两种PointcutAdvisor,分别是:

 ①org.springframework.aop.support.RegexpMethodPointcutAdvisor  (需要加上完整类名,可以用Spring提供的匹配方式)

 ②org.springframework.aop.support.NameMatchMethodPointcutAdvisor(只需要方法名,不用加类名)

今天,主要来说明下RegexpMethodPointcutAdvisor的用法。贴一个例子来说明,一些说明都写在注释中~看贴的代码:

/**
 * 打印接口
 * @author ChenST
 */
public interface IPrinter {
	
	/** 打印接口 */
	public void print();
}
/**
 * 打印接口实现类
 * @author ChenST
 */
@Repository
public class PrinterImpl implements IPrinter{
	
	@Override
	public void print() {
		System.out.println("hello world");
	}
}
/**
 * 通知,在执行方法执行后调用该方法
 * @author ChenST
 */
//对应的还有MethodBeforeAdvice等
public class AfterPrinter implements AfterReturningAdvice{
    
	//第一个参数表示 切入方法的 [返回对象]
	//第二个参数表示 切入方法的 [方法反射对象]
	//第三个参数表示 切入方法的 [参数数组(方法的所有参数组成)]
	//第四个参数表示 [调用该方法的对象]
	@Override
	public void afterReturning(Object returnObject, Method method,
			Object[] argArray, Object callObject) throws Throwable {
		System.out.println("add log:print Hello world");
	}
}
<context:annotation-config />
      <context:component-scan base-package="com.shine" />        
      
      <bean id="printer" class="com.shine.PrinterImpl"/>     
      <bean id="afterPrinter" class="com.shine.AfterPrinter"/>
      
      <!-- 配置一个拦截器 (切入点对象,确定何时何地调用拦截器)  -->
      <bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
      	 <!-- [通知,特定连接点所采取的动作] -->
      	 <!-- 加入切面,切面为当执行完print方法后 再执行加入的切面 -->
      	 <property name="advice">
      	 	<ref local="afterPrinter"/>
      	 </property>
      	 <!-- 要拦截的方法,可根据Spring提供匹配方式进行拦截  -->
      	 <property name="pattern">
      	 		<!--  .表示符合任何单一字元
      	 		 ###  +表示符合前一个字元一次或多次
      	 		 ###  *表示符合前一个字元零次或多次
      	 		 ###  \Escape任何Regular expression使用到的符号
      	 		 -->
      	 		 <!-- .*表示前面的前缀(包括包名) 表示print方法-->
      	 		<value>.*print</value>
      	 </property>
      </bean>
      
      <!-- ### 代理工程  -->
      <bean id="proxyFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
      	 <!-- 指定目标对象,目标对象是PrinterImpl对象 -->
      	 <property name="target">
			<ref local="printer"/>
		 </property>
		
		 <!-- 该目标中加入拦截器pointcutAdvisor -->
		 <property name="interceptorNames">
			<list>
				<value>pointcutAdvisor</value>
			</list>
		 </property>
      </bean>
/**
 * 测试类
 */
public class TestMain {

	public static void main(String[] args) {
		ApplicationContext ctx = new FileSystemXmlApplicationContext(
				"classpath:applicationContext.xml");
		// ☆注意:这里的Bean对象获取必须从代理工厂中去取,否则无法切入
		IPrinter printer = (IPrinter) ctx.getBean("proxyFactory");
		printer.print();
	}
}
hello world
add log:print Hello world

 感觉这么多开源东西中,最需要研究的就是Spring了~~ = =|| 继续study~~

No Hibernate Session bound to thread,and configuration does not allow creation错误解决方案

忽遇如下错误:

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

百度得解:

因:将对应操作入事务即可

方法:于Spring配置文件的事务拦截器中加入对应的类即可~

出现Unable to find Bean with name XXX 可能原因

今天在公司做项目中,

由Ctrl层 >>> Biz层>>> Dao层 。在Ctrl注入Biz层类 ,在Biz层类注入Dao层类,使用注解的方式进行注入,例如,在Biz层注入Dao层的时候,首先需要将Dao层暴露出来,也就是将这个类标上 @Repository等注解,不然可能会出现Unable to find Bean with name的错误。原理实际上就是定义了一个Bean,没有定义的Bean使用注入自然是找不到这个Bean对象咯~~

换句话说若出现:Unable to find Bean with name 这种错误,不妨看看被注入类是否暴露给其他类咯~~也许是这个问题~

Spring中ApplicationContextAware接口用法

加载Spring配置文件时,如果Spring配置文件中所定义的Bean类,如果该类实现了ApplicationContextAware接口,那么在加载Spring配置文件时,会自动调用ApplicationContextAware接口中的

public void setApplicationContext(ApplicationContext context) throws BeansException

方法,并且自动可获得ApplicationContext 对象。前提必须在Spring配置文件中指定改类。

一个Demo程序如下:

 

Spring配置文件中配置:

<bean id="springContext" class="com.shine.spring.SpringContextHelper"></bean>

 

/**
 * ApplicationContext的帮助类
 * 自动装载ApplicationContext
 * 
 * @author ChenST
 * @create 2010-6-24
 * 
 */
public class SpringContextHelper implements ApplicationContextAware {
	
	private static ApplicationContext context ;
	
	/*
	 * 注入ApplicationContext
	 */
	@Override
	public void setApplicationContext(ApplicationContext context)
			throws BeansException {
		//在加载Spring时自动获得context
		SpringContextHelper.context = context;
		System.out.println(SpringContextHelper.context);
	}
	
	public static Object getBean(String beanName){
		return context.getBean(beanName);
	}
}

Spring中抽象类不能注入属性

昨天做项目的时候,因为注入用的很爽,自然而然的在什么类下都用注入,结果出问题,注入的属性都是为null,查了一下,会不会是抽象类的问题呢,百度之,得下面的答案:

抽象类不能生成实例对象,spring无法注入 。
因为spring的原理是启动服务器时读取配置文件,取得类名后利用反射机制在spring上下文中生成一个单例的对象,由spring注入属性并维护此对象的状态,抽象类在反射生成对象时就已经失败了,后面的不会进行。

Spring2.5常用注解

公司代码很多Spring注解,下午花了点时间学习了下。贴个小案例来使用这些注解:

学习的注解列表

@Component
@Service
@Repository
@Controller
@Autowired
@Qualifier
@Resource
@PostConstruct
 @PreDestroy

1.水果接口

/**
 * 水果接口
 * 
 * @author ChenST
 *
 */
public interface Fruit {
	/**
	 * 获取水果的名字
	 * @return
	 */
	public String getName();
}

2.苹果类

/**
 * 苹果
 * @author ChenST
 *
 */
@Service
public class Apple implements Fruit {

	/*
	 * (non-Javadoc)
	 * @see com.spring.annotation.Fruit#getName()
	 */
	public String getName() {
		return "苹果";
	}
}

3.梨类

/**
 * 梨
 * @author ChenST
 *
 */
@Service
public class Pear implements Fruit {

	public String getName() {
		return "梨";
	}
}

4.人类

/**
 * 人,拥有水果
 * @author ChenST
 *
 */
/*
 * @Service表示定义个Bean对象默认Bean的名字为
 * 类的第一个字母小写名字,例如Human为human
 * @Service("name")也可以自己指定名字name
 * ----------以下四种注解是等效的,只是意义不同
 * @Component 比较中立的类进行注释
 * @Service   表示服务层
 * @Repository表示持久层
 * @Controller表示控制层
 */
@Service
public class Human {
	
	/** 表示拥有水果 */
	/*@Autowired(required = false)表示不确定
	 *Spring容器中拥有某个类的Bean时候,这样Spring
	 *找不到这个Bean时也不报错
	 *
	 *@Autowired注解表示自动注入对象。在配置文件中需要配置
	 *<context:annotation-config />
     *<context:component-scan base-package="*"/>
     *
     *@Qualifier("apple")表示当这个fruit有两个实现类时
     *指定其中一个。负责会报错
     *
     *@Resource注释与Autowired差不多,Resource可以通过
     *name和type来指明要注入的类
	 */
	//@Autowired
	//@Qualifier("apple")
	//@Resource(name="apple")
	@Resource(type=Apple.class)
	private Fruit fruit;
	
	public void setFruit(Fruit fruit) {
		this.fruit = fruit;
	}

	/**
	 * 吃水果
	 */
	public void eat(){
		System.out.println("我在吃"+this.fruit.getName());
	}
	
	/*
	 * @PostConstruct表示Spring容器初始化后调用
	 */
	@PostConstruct
	public void eatBegin(){
		System.out.println("我要开始吃了");
	}
	
	/*
	 * @PreDestroy表示Spring容器销毁之前调用
	 */
	@PreDestroy
	public void eatFinish(){
		System.out.println("吃完了");
	}
}

5.测试类

/**
 * 测试类
 * 
 * @author ChenST
 *
 */
public class Test {
	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
		Human human=(Human) ctx.getBean("human");
		human.eat();
	}
}

5.applicationContext.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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
	
    <!-- 自动注解配置 -->
    <context:annotation-config />
    <context:component-scan base-package="*" />
</beans>

6.运行结果

我要开始吃了
我在吃苹果
吃完了

Spring中进行测试-Junit框架的扩展

利用Spring来进行测试。首先看看下面几个类。【参照网上的资料】

1.AbstractSpringContextTests 类是针对所有测试情景的类,一般不使用,而是使用他们的之类。

2.AbstractDependencyInjectionSpringContextTests 类继承了AbstractSpringContextTests是一个针对所有测试的超类,具体依赖于Spring上下文。可支持依赖注入

3.AbstractTransactionalSpringContextTests 类继承了AbstractDependencyInjectionSpringContextTests,继承该类的测试用例在spring管理的事务中进行,测试完后对数据库的记录不会造成任何影响。你对数据库进行一些操作后,它会自动把数据库回滚,这样就保证了你的测试对于环境没有任何影响。

测试用例需实现protected abstract String[] getConfigLocations()方法来获取上下文。

1.创建一个测试类的基类

/**
 * Spring测试的基类
 * 
 * @author ChenST
 *
 */
public abstract class BaseSpringTest extends AbstractDependencyInjectionSpringContextTests {
	
	/**
	 * 获取上下文spring context,该路径是在classpath下的路径
	 */
	public String[] getConfigLocations() {
		String[] configLocations = { "applicationContext.xml" };
		return configLocations;
	}
}

2.测试用例

/**
 * 测试用例,测试Dao的方法getUserInfo(String userCode)
 * 
 * @author ChenST
 *
 */
public class UsersDaoTest extends BaseSpringTest {
	
	/** 用户Dao,注入实现 */
	private UsersDAO userDao;
	
	public void setUserDao(UsersDAO userDao) {
		this.userDao = userDao;
	}

	/**
	 * 测试getUserInfo()方法,需要以test开头
	 */
	public void testGetUserInfo(){
		Users user=userDao.getUserInfo("004");
	    System.out.println(user.getDepartment().getDepName());
	}
}

可通过MyEclipse中的Outline透视图中进行单个方法的测试或者整个类的测试。