In MyInterceptor, which interface, Ifclocal or IfcRemote, invoked the method doStuff in MyEjb? It's possible know through which "channel" your bean was called?
I need know which dependency injection invoked the method.
#Local
public interface IfcLocal {
void doStuff(String s);
}
#Remote
public interface IfcRemote {
void doStuff(String s);
}
#Stateless
#Interceptors({ MyInterceptor.class })
public class MyEjb implements IfcLocal, IfcRemote {
#Override
public void doStuff(String s) {
System.out.println(s);
}
}
public class MyManagedBean {
#EJB private ifcLocal ifcLocal;
#EJB private IfcRemote ifcRemote;
public void go() {
ifcLocal.doStuff("xxx");
ifcRemote.doStuff("xxx");
}
}
public class MyInterceptor {
#AroundInvoke
public Object intercept(InvocationContext inv) throws Exception {
// ??? who invoked ???
System.out.prinln(inv.getTarget().getClass()); // print MyEjb
}
}
Inject #Resource private SessionContext sessionContext; in MyInterceptor. After: Class<?> interfaceReference = sessionContext.getInvokedBusinessInterface().
Related
My service has a #Controller with multiple APIs.
Each API accepts a specific kind of object.
I would like to inject a single interface into a controller class, but have different implementations of the interface depending on the type of the input argument - is that possible?
#Controller
public class ApiClass{
private final Service service;
public ApiClass(Service service) {
this.service = service;
}
public ResponseEntity<Response> apiFirst (Object1 object1) {
return ResponseEntity.ok(service.process(object1));
}
public ResponseEntity<Response> apiTwo (Object2 object2) {
return ResponseEntity.ok(service.process(object2));
}
}
public interface Service <T extends OwnObjectClass>{
void process (T object);
}
public class Implementation1 implements Service {
#Override
void process (Object1 object) {
--some code;
}
}
public class Implementation2 implements Service {
#Override
void process (Object2 object) {
--some code;
}
}
How to do it correctly so that for each implementation not to add a new injection to the ApiClass?
Spring will provide the primary bean for the interface implementation unless you use the #Qualifer annotation with the desired instance. The injected bean can not mutate to another instance.
If you don't want to use multiple injections in the controller, you can create a ServiceProvider and ask for a specific implementation each time.
Here is an example:
public class ApiClass{
private final ServiceProvider provider;
public ApiClass(ServiceProvider provider) {
this.provider = provider;
}
public ResponseEntity<Response> apiFirst (Object1 object1) {
return ResponseEntity.ok(provider.getService("Implementation1").process(object1));
}
public ResponseEntity<Response> apiTwo (Object2 object2) {
return ResponseEntity.ok(provider.getService("Implementation2").process(object1));
}
}
#org.springframework.stereotype.Service
public class ServiceProvider {
private Map<String, Service> services;
public ServiceProvider(List<Service> services) {
this.services = services.stream()
.collect(java.util.stream.Collectors.toMap(
Service::type,
service -> service
)
);
}
public Service getService(String type) {
return services.get(type);
}
}
interface Service<T extends OwnObjectClass> {
String type();
void process(T object);
}
#org.springframework.stereotype.Service("Implementation1")
class Implementation1 implements Service {
#Override
public String type() {
return "Implementation1";
}
#Override
public void process(OwnObjectClass object) {
}
}
#org.springframework.stereotype.Service("Implementation2")
class Implementation2 implements Service {
#Override
public String type() {
return "Implementation2";
}
#Override
public void process(OwnObjectClass object) {
}
}
You can change the string in the type for an Enum.
There is another way using HandlerMethodArgumentResolver where you can inject your dependency directly into the method definition.
Here is a nice article explaining it: https://reflectoring.io/spring-boot-argumentresolver/
Is there a (clean) way to provide an initialized Spring Bean as a service implementation in a Java 9 module ?
The best solution I think of right now is :
module-info.java
module my.module {
provides Service with ServiceWrapper;
}
Service.java
public interface Service {
void doSomething();
}
ServiceImpl.java
#Component
public class ServiceImpl implements Service, InitializingBean {
#Autowired SomeDependency dep;
public void doSomething() {
...
}
#Override
public void afterPropertiesSet() throws Exception {
ServiceWrapper.INSTANCE.set(() -> this);
}
}
ServiceWrapper.java
public class ServiceWrapper implements Service {
static final ServiceWrapper INSTANCE = new ServiceWrapper();
private Supplier<Service> supplier;
void set(Supplier<Service> supplier) {
this.supplier = supplier;
}
public void doSomething() {
this.supplier.get().doSomething();
}
public static Service provider() {
return ServiceWrapper.INSTANCE;
}
}
So I want to achieve something like this:
#Component
public class ComponentA {
public void doThis(){};
}
#Component
public class ComponentB {
public void doThat(){};
}
public interface MyInterface {
void doSomething();
}
public class MyInterfaceImplA implements MyInterface {
private final ComponentA componentA;
#Inject
public MyInterfaceImplA(ComponentA componentA){
this.componentA = componentA;
}
public void doSomething(){
componentA.doThis();
}
}
public class MyInterfaceImplB implements MyInterface {
private final ComponentB componentB;
#Inject
public MyInterfaceImplB(ComponentB componentB) {
this.componentB = componentB;
}
public void doSomething() {
componentB.doThat();
}
}
What I basically want is to inject different components into different classes implementing the same interface.
My question is if there is a good way to set this architecture up in this or a similar way? Or is there a pattern to achieve this in a better way?
I want to use #Qualifier to dynamically specifying parameters? how to do it ?
#Qualifier("two") 'two' as a parameter ,can be 'one' 'three' or other.
Can i use aop dynamically design 'two'?
means I want to change the name of service with a #Qualifier by parameters.
the parameter from the url 'Token'.
case: url: http://localhost:8080/insert/order, token has a parameter: companyId = one
#RestController
public class ApiWebService {
#Autowired
#Qualifier("two")
//#Qualifier("one")
private BaseService baseService;
#GetMapping("insert/order")
public void test() {
baseService.insertOrder();
}
}
#Service("one")
public class CompanyOneService extends BaseService {
#Override
public void insertOrder() {
System.out.println("conpanyOne");
System.out.println("baseInsertOrder");
}
}
#Service("two")
public class CompanyTwoService extends BaseService {
#Override
public void insertOrder(){
System.out.println("companyTwo");
System.out.println("baseInsertOrder");
}
}
three
four
...
#Service
public class BaseService {
public void insertOrder(){
System.out.println("baseInsertOrder");
}
}
你好 !
No you cannot , mostly because the attribute in Java annotation does not allow to assign with variables.
Actually you want to choose an implementation to use based on some runtime conditions(i.e.companyId in your case). You can achieve it using factory pattern with #Configuration and #Bean which is much more elegant and easier to understand than your ugly AOP solution:
First define a factory:
#Configuration
public class ServiceFactory{
#Bean
public BaseService companyOneService(){
return new CompanyOneService();
}
#Bean
public BaseService companyTwoService(){
return new CompanyTwoService();
}
public BaseService getService(Integer companyId){
if(companyId == 1){
return companyOneService();
}else if(company==2){
return companyTwoService();
}else{
//blablablab
}
}
}
In the controller , inject the ServiceFactory to get the related Service based on the the company Id
#RestController
public class ApiWebService {
#Autowired
private ServiceFactory serviceFactory;
#GetMapping("insert/order")
public void test() {
Integer companyId = getCompanyIdFromToken(httpServletRequest);
BaseService service = serviceFactory.getService(companyId);
service.blablabla();
}
}
Inject (autowire) ApplicationContext into your class and use one of getBeans* method to find the exact bean you need.
aspect
#Aspect
#Component
public class ApiAspect {
#Pointcut("execution(* com.example.demo.control.ApiWebService.*(..))")
public void apiInputWebService() {
}
#Before("apiInputWebService()")
public void apiInputAuth(JoinPoint joinPoint) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes())
.getRequest();
String token = request.getHeader("Authorization");
//compangId can be from token
String compangId = "one";
Object target = joinPoint.getTarget();
Method method = target.getClass().getMethod("before", String.class);
method.invoke(target, compangId);
}
}
control
#RestController
public class ApiWebService {
private ApiService baseService;
#Autowired
private ApplicationContext applicationContext;
public void before(String company) {
baseService = (ApiService) applicationContext.getBean(company);
}
#GetMapping("insert/order")
public void test() {
baseService.insertOrder();
}
}
service
#Service
public class ApiService {
public void insertOrder(){
System.out.println("baseInsertOrder");
}
}
#Service("one")
public class CompanyOneService extends ApiService {
#Override
public void insertOrder() {
System.out.println("conpanyOne");
System.out.println("baseInsertOrder");
}
}
#Service("two")
public class CompanyTwoService extends ApiService {
#Override
public void insertOrder(){
System.out.println("companyTwo");
System.out.println("baseInsertOrder");
}
}
I am trying to implement a generic abstract class in my service layer. I am already using a simliar pattern in my dao layer and it works fine. I found a working example in the Spring in Practice v8 ebook. I am wondering if there is a way to autowire the following working code. (The code works but I have to call my helper method 'setDao' before I use any other method in the class)
Test class:
public class App {
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext("classpath:/applicationContext.xml");
MyService service = (MyService)appContext.getBean("myService");
service.setDao();
Heading detail = new Heading();
detail.setName("hello");
service.save(detail);
Heading dos = service.findById(Long.valueOf(1));
System.out.println(dos);
}
}
MyServiceImpl class
#Service("myService")
public class MyServiceImpl extends AbstractServiceImpl<Heading> implements HeadingService {
#Autowired
private HeadingDao headingDao;
public void setHeadingDao(HeadingDao headingDao) {
this.headingDao = headingDao;
}
public void setDao() {
super.setDao(this.headingDao);
}
}
MyService interface
public interface HeadingService extends AbstractService<Heading> {
public void setDao();
}
AbstractServiceImpl class
#Service
public abstract class AbstractServiceImpl<T extends Object> implements AbstractService<T> {
private AbstractDao<T> dao;
public void setDao(AbstractDao<T> dao) {
this.dao = dao;
}
public void save(T t) {
dao.save(t);
}
public T findById(Long id) {
return (T)dao.findById(id);
}
public List<T> findAll() {
return dao.findAll();
}
public void update(T t) {
dao.update(t);
}
public void delete(T t) {
dao.delete(t);
}
public long count() {
return dao.count();
}
}
AbstractService interface
public interface AbstractService<T extends Object> {
public void save(T t);
public T findById(Long id);
public List<T> findAll();
public void update(T t);
public void delete(T t);
public long count();
}
Instead of having to call a method (setDao()) to allow your subclass to pass the DAO reference to your superclass, why reverse the direction and force the subclass to supply the DAO to the superclass?
for example:
public abstract class AbstractServiceImpl<T extends Object> implements AbstractService<T> {
private AbstractDao<T> dao;
abstract AbstractDao<T> getDao();
public void save(T t) {
getDao().save(t);
}
}
public class FooServiceImpl extends AbstractServiceImpl<Foo> {
#Autowired
private FooDao fooDao;
#Overrides
public AbstractDao<Foo> getDao() {
return fooDao;
}
}
There is no need to call a method externally to kick the reference-passing-chain into action.
Try making your MyServiceImpl implement InitializingBean, and change your setDao() method to be afterPropertiesSet(). It will automatically get called after the framework is done calling setters.
Or, (even more simple), just call setDao() in your setHeaderDao(...) method.
Upgrade spring framework version to 4, and the problem will be solved.
check this page.