spring.version=5.2.7.RELEASE
public class Service15 {
public Service15(){
System.out.println("service15 init");
}
}
public class Service15Factory implements FactoryBean, BeanFactoryAware {
private BeanFactory beanFactory;
public Service15Factory(){
System.out.println("serviceFactory15 init");
}
#Override
public Object getObject() throws Exception {
beanFactory.getBean("service16");
return new Service15();
}
#Override
public Class<?> getObjectType() {
return Service15.class;
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
public class Service16 {
private Service15 service15;
public Service16(){
System.out.println("service16 init");
}
public void setService15(Service15 service15){
this.service15 = service15;
}
}
configuration:
<bean id="service15" class="com.myspring.service.Service15Factory" scope="singleton">
</bean>
<bean id="service16" class="com.myspring.service.Service16">
<property name="service15" ref="service15"></property>
</bean>
I created three classes,service15 will be created by getObject of Service15Factory, and Service16 dependent on service15,service15 will be Initialized twice
Related
When I use "BeanFactoryPostProcessor" to register "BeanPostProcessor" and at the same time use AutoConfiguration to wire up my defined "BeanFactoryPostProcessor", I found that my "BeanPostProcessor" execution order is after "#PostConstruct", I am curious why, and please what is good way to deal with
The key code is as follows
JDK17\SpringBoot3.0.0-M3
#RequiredArgsConstructor
public class MapstructFactory implements BeanPostProcessor {
private final MapstructRegistry registry;
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Converter) {
registry.addConverter((Converter<?, ?>) bean);
}
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
}
#RequiredArgsConstructor
public class MapstructFactoryRegister implements BeanFactoryPostProcessor {
private final MapstructRegistry registry;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.addBeanPostProcessor(new MapstructFactory(registry));
}
}
#AutoConfiguration
public class MapstructAutoConfiguration {
#Bean
public MapstructFactoryRegister mapstructFactoryRegister(MapstructRegistry registry) {
return new MapstructFactoryRegister(registry);
}
}
I want to inject bean in a Tapestry service (not in a page).
For the moment, I use this :
public class EntityRealm extends AuthorizingRealm {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/application-context-security.xml");
SecurityServices securityServices = (SecurityServices)ctx.getBean("securityServices");
It works, but I want use this :
public class EntityRealm extends AuthorizingRealm {
#Inject
private SecurityServices securityServices;
And my applicationContext is in the web.xml.
In this second case, the injection doesn't work. Why ?
AppModule.java :
public class AppModule
{
//#Resource(name = "realm")
#Inject
private static EntityRealm realm;
#Contribute(WebSecurityManager.class)
public static void addRealms(Configuration<EntityRealm> configuration) {
//EntityRealm realm = new EntityRealm();
configuration.add(realm);
}
public static void contributeFactoryDefaults( MappedConfiguration<String, Object> configuration)
{
configuration.override(SecuritySymbols.LOGIN_URL, "/login");
configuration.override(SecuritySymbols.UNAUTHORIZED_URL, "/login");
configuration.override(SecuritySymbols.SUCCESS_URL, "/index");
configuration.override(SymbolConstants.APPLICATION_VERSION, "2.0-SNAPSHOT");
}
public static void contributeApplicationDefaults(MappedConfiguration<String, Object> configuration)
{
configuration.add(SymbolConstants.HMAC_PASSPHRASE, new BigInteger(130, new SecureRandom()).toString(32));
configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en,fr");
configuration.add( "tapestry.default-cookie-max-age", "31536000" );
}
public RequestFilter buildTimingFilter(final Logger log)
{
return new RequestFilter()
{
public boolean service(Request request, Response response, RequestHandler handler)
throws IOException
{
long startTime = System.currentTimeMillis();
try
{
return handler.service(request, response);
} finally
{
long elapsed = System.currentTimeMillis() - startTime;
log.info(String.format("Request time: %d ms", elapsed));
}
}
};
}
public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration,
#Local
RequestFilter filter)
{
configuration.add("Timing", filter);
}
}
And the EntityRealm.java :
public class EntityRealm extends AuthorizingRealm {
//***************************************
//************* Attributes *************
//***************************************
//ApplicationContext ctx = new ClassPathXmlApplicationContext("/application-context-security.xml");
//SecurityServices securityServices = (SecurityServices)ctx.getBean("securityServices");
//#Resource(name = "securityServices")
#Inject
private SecurityServices securityServices;
//***************************************
//************ Constructors *************
//***************************************
public EntityRealm() {
super(new MemoryConstrainedCacheManager());
setName("myapprealm");
setAuthenticationTokenClass(UsernamePasswordToken.class);
}
//***************************************
//********** Public Methods *************
//***************************************
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) throw new AuthorizationException("PrincipalCollection was null, which should not happen");
application-context.xml :
<bean id="realm" class="net.atos.m2m.telecom.ihm.services.EntityRealm">
<property name="securityServices" ref="securityServices"></property>
</bean>
<bean id="securityServices" class="net.atos.m2m.telecom.ihm.applicatif.services.security.impl.SecurityServicesImpl">
<property name="servicesTelSecu" ref="servicesTelSecu"></property>
<property name="converterSecDSPtoDTO" ref="converterSecDSPtoDTO"></property>
<property name="converterSecDTOtoDSP" ref="converterSecDTOtoDSP"></property>
</bean>
Can you help me ?
Thank you.
How i say in previous comment, if you create EntityRealm in this way .. new EntityRealm() the inject\autowire does not work.
You must define EntityRealm as bean .. XML or Annotation.
<bean id="entityRealm" class="package.EntityRealm"/>
<bean id="securityServices" class="package.SecurityServices"/>
You can use #Resource instead,
#Resource(name = "securityServices")
private SecurityServices securityServices;
And make sure that application-context-security.xml file is loaded by Spring.
I have an Aspect that intercepts a method a() decorated with the annotation #Foo. This method
calls another method b() also decorated with the annotation #Foo. I want my aspect to intercept
only a() and not b(). How can I do this?
I have tried within() but with no success. With ThreadLocal it works but I am wondering
if there is a Spring solution.
#Component
#Order(value = 2)
#Aspect
public class FooAspect {
#Around(value = "#annotation(Foo)")
public Object aroundAdvice(ProceedingJoinPoint pjp, Foo foo) {
...
}
}
I don't think there is a way to do that with Aspects only. The interception will happen no matter what.
However, one solution is to use a ThreadLocal Byte to flag that you've already applied the advice.
private ThreadLocal<Byte> flag = new ThreadLocal<>();
#Around(value = "#annotation(Foo)")
public Object aroundAdvice(ProceedingJoinPoint pjp, Foo foo) throws Throwable {
if (flag.get() == null) {
try {
flag.set((byte) 1); // or 0, whatever
// apply advice
return pjp.proceed();
} finally {
flag.remove();
}
} else {
// don't apply advice
return pjp.proceed();
}
}
You'll need to catch or throws the Throwable thrown from proceed().
There is a cflow like poincut in the framework, but it's not supported by Aspectj-style.
However you can use it with aop-schema configuration approach.
For example:
public class NotInFlowPoincut extends AspectJExpressionPointcut {
private ControlFlowPointcut cflow;
private Class<?> flowClass;
private String methodName;
public NotInFlowPoincut() {
}
#PostConstruct
public void init() {
cflow = new ControlFlowPointcut(flowClass, methodName);
}
#Override
public boolean matches(Method method, Class targetClass, Object[] args) {
return super.matches(method, targetClass, args) && ! cflow.matches(method, targetClass, args);
}
public Class<?> getFlowClass() {
return flowClass;
}
public void setFlowClass(Class<?> flowClass) {
this.flowClass = flowClass;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
}
public class FooAspect {
public Object aroundAdvice(ProceedingJoinPoint pjp, Foo foo) throws Throwable {
System.out.println("in aroundAdvice");
return pjp.proceed();
}
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/context.xml");
A a = ctx.getBean(A.class);
a.a();
B b = ctx.getBean(B.class);
b.b();
}
}
public class A {
#Autowired B b;
#Foo
public void a() {
System.out.println("In a()");
b.b();
}
}
public class B {
#Foo
public void b() {
System.out.println("In b()");
}
}
And finally
<bean id="cflow" class="test.NotInFlowPoincut">
<property name="expression" value="#annotation(foo) " />
<property name="methodName" value="aroundAdvice" />
<property name="flowClass" value="test.FooAspect" />
</bean>
<bean id="foo" class="test.FooAspect" />
<bean id="a" class="test.A" />
<bean id="b" class="test.B" />
<aop:config proxy-target-class="true">
<aop:aspect ref="foo" >
<aop:around method="aroundAdvice" pointcut-ref="cflow" />
</aop:aspect>
</aop:config>
Will output:
in aroundAdvice
In a()
In b()
in aroundAdvice
In b()
That seems to be what you are looking for.
Note that cflow poincut is slow, see javadoc.
I have a session scoped bean in which I hold some user data and want to write a unit test to ensure that it remains session scoped.
I want to mock the starting and the ending of a session in jUnit and compare the values of the session scoped bean.
For now I have the following (rough drafts) of the unit test:
I use a custom context loader to register the session scope.
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.test.context.support.GenericXmlContextLoader;
import org.springframework.web.context.request.SessionScope;
public class SessionScopedGenericXmlContextLoader extends GenericXmlContextLoader {
#Override
protected void customizeBeanFactory(final DefaultListableBeanFactory beanFactory) {
beanFactory.registerScope("session", new SessionScope());
super.customizeBeanFactory(beanFactory);
}
}
The unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = SessionScopedGenericXmlContextLoader.class,
locations = { "classpath:/test-applicationContext.xml","classpath:/cache-config.xml" })
public class CachingTest extends AbstractJUnit4SpringContextTests {
// Session scoping
protected MockHttpSession session;
protected MockHttpServletRequest request;
ApplicationContext ctx;
CacheManager cacheManager;
Cache accountSettingsCache;
#Autowired
#Qualifier("cachedMethods")
DummyCacheMethods methods;
#Autowired
private SecurityContextHolder contextHolder;
protected void startRequest() {
request = new MockHttpServletRequest();
request.setSession(session);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
}
protected void endRequest() {
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).requestCompleted();
RequestContextHolder.resetRequestAttributes();
request = null;
}
protected void startSession() {
session = new MockHttpSession();
}
protected void endSession() {
session.clearAttributes();
session = null;
}
#Before
public void constructSession() {
ctx = applicationContext;
cacheManager = (CacheManager) ctx.getBean("cacheManager");
accountSettingsCache = cacheManager.getCache("accountSettingsCache");
startRequest();
startSession();
}
#After
public void sessionClean() {
endRequest();
endSession();
contextHolder.clearContext();
}
#Test
// #DirtiesContext
public void checkSession1() {
final Authentication authentication = new Authentication() {
public String getName() {
return "Johny";
}
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
}
public boolean isAuthenticated() {
return true;
}
public Object getPrincipal() {
final CustomUserDetails pr = new CustomUserDetails();
pr.setUsername("Johny");
return pr;
}
public Object getDetails() {
return null;
}
public Object getCredentials() {
return null;
}
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
};
contextHolder.getContext().setAuthentication(authentication);
assertTrue(methods.getCurrentUserName().equals(accountSettingsCache.get("currentUser").get()));
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication.getPrincipal()).getUsername()));
}
#Test
// #DirtiesContext
public void testSession2() {
final Authentication authentication2 = new Authentication() {
public String getName() {
return "James";
}
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
}
public boolean isAuthenticated() {
return true;
}
public Object getPrincipal() {
final CustomUserDetails pr = new CustomUserDetails();
pr.setUsername("James");
return pr;
}
public Object getDetails() {
return null;
}
public Object getCredentials() {
return null;
}
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
};
SecurityContextHolder.setContext(contextHolder.getContext());
SecurityContextHolder.clearContext();
SecurityContextHolder.getContext().setAuthentication(authentication2);
assertTrue(methods.getCurrentUserName().equals(accountSettingsCache.get("currentUser").get()));
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication2.getPrincipal()).getUsername()));
}
#Test
public void testWiring() {
assertTrue("cacheManager is null", cacheManager != null);
assertTrue("accountSettingsCache is null", accountSettingsCache != null);
}
}
And the relevant function from DummyCachedMethods is:
#Cacheable(value = { "accountSettingsCache" }, key = "new String(\"currentUser\")")
public String getCurrentUserName() {
return WebUtils.getCurrentUser().getUsername();
}
WebUtils.getCurrentUser() just returns the current Principal from the SecurityContext.
But this does not work, the test does not pass at this line:
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication2.getPrincipal()).getUsername()));
Since the method call is cached, it still returns Johnny instead of James.
My cache related beans are:
<bean id="accountSettingsCache" class="org.springframework.cache.concurrent.ConcurrentMapCache" scope="session">
<constructor-arg>
<value>accountSettingsCache</value>
</constructor-arg>
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="defaultCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="defaultCache" />
<bean id="cacheManager" class="my.package.DynamicCacheManager" scope="session">
<property name="caches">
<set>
<ref bean="accountSettingsCache" />
<ref bean="defaultCache" />
</set>
</property>
<aop:scoped-proxy />
</bean>
accountSettingsCache is session scoped, I want to start a new session and destroy it.
It does pass if I uncomment the DirtiesContext annotations, but I guess it's not what I want.
What am I missing ?
I want to create a bean that will act as a provider.
I will give it the class that it should return and the list of properties that I should set before returning it.
so basically it looks like this:
<bean id="somethingFactory" class="foo.bar.SomethingFactory">
<property name="implClass" value="foo.bar.SomehtingImpl" />
<property name="properties">
<props>
<prop key="prop1">prop1Value</prop>
<prop key="prop2">prop2Value</prop>
</props>
</property>
</bean>
The "SomethingFactory" has a provide() method that will return an instance of "SomehtingImpl".
The question is how can I use Spring to do it?
Make SomethingFactory a FactoryBean, extend AbstractFactoryBean and use a BeanWrapper to populate the properties from the input parameters.
Here's a sample implementation:
public class ServiceFactoryBean<T> extends AbstractFactoryBean<T> {
private Class<T> serviceType;
private Class<? extends T> implementationClass;
private Map<String, Object> beanProperties;
#Override
public void afterPropertiesSet() {
if (serviceType == null || implementationClass == null
|| !serviceType.isAssignableFrom(implementationClass)) {
throw new IllegalStateException();
}
}
#Override
public Class<?> getObjectType() {
return serviceType;
}
public void setBeanProperties(final Map<String, Object> beanProperties) {
this.beanProperties = beanProperties;
}
public void setImplementationClass(
final Class<? extends T> implementationClass) {
this.implementationClass = implementationClass;
}
public void setServiceType(final Class<T> serviceType) {
this.serviceType = serviceType;
}
#Override
protected T createInstance() throws Exception {
final T instance = implementationClass.newInstance();
if (beanProperties != null && !beanProperties.isEmpty()) {
final BeanWrapper wrapper = new BeanWrapperImpl(instance);
wrapper.setPropertyValues(beanProperties);
}
return instance;
}
}