I have a little experience with java Servlets and JSP, but i worked with Spring. In Spring we have interface named BeanPostProcessor. I used this interface implementation to create custom annotations. Code example
public class InjectRandomIntAnnotationBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String string) throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
InjectRandomInt annotation = field.getAnnotation(InjectRandomInt.class);
if (annotation != null) {
int min = annotation.min();
int max = annotation.max();
Random r = new Random();
int i = min + r.nextInt(max - min);
field.setAccessible(true);
ReflectionUtils.setField(field, bean, i);
}
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object o, String string) throws BeansException {
return o;
}
}
When i begin working with servlets i mentioned this annotation #WebServlet(urlPatterns = "/user/login")
The question is: is it any functional in servlets that is similar with BeanPostProcessor in Spring and where is this annotation #WebServlet injected?
Example:
I mean annotations in Spring are injected with BeanPostProcessor, for examle annotation #AutoWired is declared by AutoWiredAnnotationBeanPostProcessor class, but where the annotation #WebServlet is injected(or declared)
Servlets are not managed by Spring Container. So their annotations are processed by the servlet api implementation they run on, i.e. Tomcat.
Depending on what you want to achieve, you can simply extends HttpServlet and override the init method with your logic.
If you need to do some wiring you can also take advantage of SpringBeanAutowiringSupport. See also
Spring service not injected in web servlet
I want to inject an object in servlet using Spring
Related
I am converting my existing Spring Application to a Spring Boot Application. In my existing application, we have the need to connect to multiple databases and we had achieved this by having multiple data sources defined and fetching the corresponding bean based on the condition. The transaction manager were also selected using a custom implementation of TransactionInterceptor.
#Override
public TransactionAttributeSource getTransactionAttributeSource() {
final TransactionAttributeSource origTxAttrSource = super.getTransactionAttributeSource();
return new TransactionAttributeSource() {
#Override
public TransactionAttribute getTransactionAttribute(final Method method, final Class<?> targetClass) {
TransactionAttribute txAttr = origTxAttrSource.getTransactionAttribute(method, targetClass);
String database = (String) ThreadContext.get("database");
if (database != null && StringUtils.isNotBlank(database)) {
if (txAttr instanceof DefaultTransactionAttribute) {
((DefaultTransactionAttribute) txAttr).setQualifier("txManager" + database);
}
}
return txAttr;
}
};
}
Through a BeanFactoryPostProcessor we were including this interceptor
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] names = beanFactory.getBeanNamesForType(TransactionInterceptor.class);
for (String name : names) {
BeanDefinition bd = beanFactory.getBeanDefinition(name);
bd.setBeanClassName(MyTransactionInterceptor.class.getName());
}
}
This worked perfectly fine in Spring 4.X.
Now that we are moving towards Spring Boot, I am trying to convert the same approach. I can see that the bean factory is getting called but I don't find calls happening to the Custom Interceptor class. This results in my #Transactional to fail as there are more than one qualifying bean.
Am I missing something with regards to the Spring Boot Configuration?
(This approach of dynamic transaction management was through a reference blog http://blog.tirasa.net/dynamic-springs--at-transactional.html)
The final answer turned out to be setting the factory classes and the factory bean name to null which resulted in the transaction interceptor being invoked. I am yet to figure out how this affects the interceptor call as with the values in these fields (they point to the ProxyTransaction classes as transactionInterceptor bean is created by it).
The final code was of the form -
TransactionInterceptor Class
#Component
public class TransactionInterceptorReplacer implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory factory) throws BeansException {
String[] names = factory.getBeanNamesForType(TransactionInterceptor.class);
for (String name : names) {
BeanDefinition bd = factory.getBeanDefinition(name);
bd.setBeanClassName(MyTransactionInterceptor.class.getName());
bd.setFactoryBeanName(null);
bd.setFactoryMethodName(null);
}
}
}
#Transactional
#Component
#EntranceLog
public class TransferServiceImpl implements TransferService {
xxxx
}
I hava a class with Transactional annotation and Component annotation. EntranceLog is my customize annotation to print log by aop.
public class LogProxyCreator extends AbstractAutoProxyCreator implements ApplicationContextAware {
private static final LogInterceptor LOG = new LogInterceptor();
private static Logger log = LoggerFactory.getLogger(LogProxyCreator.class);
#Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String s, TargetSource targetSource) throws BeansException {
Annotation anno = null;
for (Annotation annotationTemp : beanClass.getAnnotations()) {
Log temp = annotationTemp.annotationType().getAnnotation(EntranceLog.class);
if (temp != null) {
anno = temp;
break;
}
}
if (anno == null) {
return null;
}
Object[] additional = new Object[]{LOG};
log.error(beanClass.getName() + " has register the fc log.");
return additional;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
LOG.setContext(applicationContext);
}
}
When my app is starting, the bean transferServiceImpl start, but beanClass.getAnnotations() can not get any annotation. Why?
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
#Log(logName = "entrance")
public #interface EntranceLog {
#AliasFor(
annotation = Log.class,
attribute = "subLogName"
)
String logName() default "";
#AliasFor(
annotation = Log.class,
attribute = "openInfoLog"
)
boolean openInfoLog() default false;
}
This is my annotation.
In Spring #Transactionalis already an AOP processed annotation, so adding your own will require some additional work. Let me explain how Spring AOP and #Transactional works.
Spring has two ways of doing AOP, if the class implements an interface it can use a standard JDK Proxy, if the class does not implement an interface it will create a new subclass by using CGLib to emit bytecode at runtime. Unless you are very careful you will almost always get a CGLib proxy with Spring AOP.
When Spring encounters a #Transactional (class or method level) it creates a new subclass using CGLib, you can think of this class as a decorator, which forwards all calls to your implementation class. Before and after (around Advice) it check the #Transactional annotation properties, and check Thread Local storage to see if a transaction already exist, if there is no transaction it creates one, and remembers it so it can commit it afterwards. If you set a breakoint inside a Transactional method and look at the callstack you will see the call to your implementation came from the decorater class, and that there is no source code for it.
In your case the bean that is added to the Application Context, is not your TransferServiceImplbean, but the CGLib proxy created by Spring when it found the #Transactional annotation on your class, it will be named something like TransferServiceImpl$$FastClassBySpringCGLIB$$<hexstring> - This class does not have the #EntranceLog annotation, which is why your own aspect is not working.
I have never encountered this problem myself, as I try to avoid AOP in general, or at always on classes that are already being CGLib proxied by Spring. Unless you want to dig deep into the Spring source, or find someone on the Spring dev team to help you with this, I suggest that you create another layer of indirection, so you don't need to handle two aspects in the same class.
For anyone who may be unwilling or unable to alter their code structure in order to avoid this issue, the following can probably help:
As Klaus mentioned, Spring creates a decorator class when it encounters a class tagged with #Transactional. However, because this new class is just that--a decorator--you should be able to call getSuperclass() on beanClass to give you the actual class Spring is decorating, like so:
beanClass.getSuperclass().getAnnotations()
If you're using your own Annotation, ensure it also persists through runtime by annotating the Annotation class with:
#Retention(RetentionPolicy.RUNTIME)
I have tried using BeanPostProcessor
#Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException
{
log.info("postProcessBeforeInitialization bean : " + beanName);
if (bean instanceof TestAware)
{
testaware.add(((TestAware) bean).getClass());
log.info("Added testAware bean : " + beanName);
}
return bean;
}
But the problem, there are some classes which does not have bean definition.
Is there any alternative or improved way to get .
Thanks in advance.
If you want to get all instances of a subtype in the application context then it is simple enough to look through them:
#Component
public class GetSubtypesOfClass implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext ac) throws BeansException {
List<Subtype> matches = new ArrayList<>();
for (String s : ac.getBeanDefinitionNames()) {
Object bean = ac.getBean(s);
if (Subtype.class.isAssignableFrom(bean.getClass())) {
matches.add(bean);
}
}
}
}
If however you are wondering why instances of your subtype aren't getting made available in the application context then you will need to load them explicitly in a context xml, or implicitly by classpath scanning.
Assuming you are asking how to find subtypes of TestAware
As suggested by it's name, BeanPostProcessor only "processes" spring managed beans, from the doc:
Factory hook that allows for custom modification of new bean instances
So you are using the wrong tool here. You probably should use some reflection to do what you want, for example, using Reflections:
// adapted from the home page sample
Reflections reflections = new Reflections("your.package");
Set<Class<? extends TestAware>> testAwares = reflections.getSubTypesOf(TestAware.class);
NB: the above sample code will give you subtypes, not instances of subtypes.
All I wanted to find out was "all the class/methods in Spring beans which are annotated as #Versioned".
I created my custom annotation as,
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Versioned {
.....
}
This annotation works perfectly when I use Java reflection to find methods as:
for(Method m: obj.getClass().getMethods()){
if(m.isAnnotationPresent(Versioned.class)){
.... // Do something
}
But it does not work when I access Spring beans and try similar check:
public class VersionScanner implements ApplicationContextAware{
public void setApplicationContext(ApplicationContext applicationContext){
for(String beanName: applicationContext.getBeanDefinitionNames()){
for(Method m: applicationContext.getBean(beanName).getClass().getDeclaredMethods()){
if(m.isAnnotationPresent(Versioned.class){
// This is not WORKING as expected for any beans with method annotated
}
}
}
}
}
In fact, this code does find other annotations such as #RequestMapping. I am not sure what I am doing wrong with my custom annotation.
Going through your code, I figured out that you are using Spring AOP with CGLIB Proxying. Due to which your classes (which have methods annotated with #Versioned ) are being proxied.
I have tested this solution with your code base.
Use the following code, and it should resolve your issue. Look for more options below the code snippet:
#Configuration
public class VersionScanner implements ApplicationContextAware {
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
for (String beanName : applicationContext.getBeanDefinitionNames()) {
Object obj = applicationContext.getBean(beanName);
/*
* As you are using AOP check for AOP proxying. If you are proxying with Spring CGLIB (not via Spring AOP)
* Use org.springframework.cglib.proxy.Proxy#isProxyClass to detect proxy If you are proxying using JDK
* Proxy use java.lang.reflect.Proxy#isProxyClass
*/
Class<?> objClz = obj.getClass();
if (org.springframework.aop.support.AopUtils.isAopProxy(obj)) {
objClz = org.springframework.aop.support.AopUtils.getTargetClass(obj);
}
for (Method m : objClz.getDeclaredMethods()) {
if (m.isAnnotationPresent(Versioned.class)) {
//Should give you expected results
}
}
}
}
}
To detect a proxy class:
For Spring AOP proxy using any proxying mechanism use org.springframework.aop.support.AopUtils#isAoPProxy
If you are proxying with Spring CGLIB (not via Spring AOP), use org.springframework.cglib.proxy.Proxy#isProxyClass
If you are proxying using JDK Proxy, use java.lang.reflect.Proxy#isProxyClass
I have just written one if condition which is sufficient in your case; but in case multiple proxying utilities are used, multiple if-else conditions will have to be written based on the information above.
applicationContext.getBean(beanName).getClass() gives you the proxied class that Spring creates around your target class.
What you want is to get hold of the target class, if any, from your Spring bean.
Spring provides a nice utility class for resolving this called AopUtils.class.
Below is how you would use it:
for(String beanName: applicationContext.getBeanDefinitionNames()){
Method[] methods = AopUtils.getTargetClass(applicationContext.getBean(beanName)).getDeclaredMethods();
for(Method m: methods){
if(m.isAnnotationPresent(Versioned.class){
}
}
}
Note that you will have to import the spring-aop Maven dependency to get hold of the AopUtils class:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
for (String beanName : applicationContext.getBeanDefinitionNames()) {
Object obj = applicationContext.getBean(beanName);
Class<?> objClz = obj.getClass();
if (org.springframework.aop.support.AopUtils.isAopProxy(obj)) {
objClz = org.springframework.aop.support.AopUtils.getTargetClass(obj);
}
for (Method m : objClz.getDeclaredMethods()) {
if (m.isAnnotationPresent(Transactional.class)) {
Transactional transactional = m.getAnnotation(Transactional.class);
Class<? extends Throwable>[] value = transactional.rollbackFor();
if (value == null){
// help !!!
// If value is null, I want to set a value for him like Exception.class How can I modify it?
}
}
}
}
}
Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}